/* Part of the EDG Compiler Project, under the Apache License v2.0 with LLVM Exceptions. See https://edgcpp.org/LICENSE.txt for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ /* il.c -- Construction of intermediate language trees. */ /* Header files common to all files. */ #include "fe_common.h" #ifdef PCH_PRAGMA_GUARD /* Mark the end of the sequence of headers subject to precompiled header processing. */ #pragma hdrstop #endif /* ifdef PCH_PRAGMA_GUARD */ /* Additional header files. */ #include "expr.h" #include "exprutil.h" #include "folding.h" #include "il_walk.h" #include "modules.h" #if STANDALONE_IL_DISPLAY || \ (NONCLASS_TEMPLATE_INSTANTIATIONS_IN_SOURCE_SEQUENCE_LISTS && \ IL_SHOULD_BE_WRITTEN_TO_FILE) #include "il_read.h" #endif /* STANDALONE_IL_DISPLAY || ... */ #if !STANDALONE_UTILITY_PROGRAM #include "func_def.h" #include "interpret.h" /* Needed for access to "alignment_of_field_full". */ #include "layout.h" #include "pch.h" #include "templates.h" #include "class_decl.h" #include "symbol_ref.h" #if MICROSOFT_EXTENSIONS_ALLOWED #include "decl_spec.h" #include "ms_attrib.h" #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #include "trans_corresp.h" #if DO_IL_LOWERING #include "lower_il.h" #include "lower_init.h" #if MINIMAL_INLINING #include "inline.h" #endif /* MINIMAL_INLINING */ #endif /* DO_IL_LOWERING */ #if MODULE_ID_NEEDED #include "lower_name.h" #endif /* MODULE_ID_NEEDED */ #include "trans_copy.h" #if GNU_FUNCTION_MULTIVERSIONING #include "sys_predef.h" #endif /* GNU_FUNCTION_MULTIVERSIONING */ /* Conditionally open the "edg" namespace. */ BEGIN_EDG_NAMESPACE /* Pointers to shared types. These are cleared by il_init. */ STATIC_THREAD a_type_ptr int_types[(int)ik_last]; STATIC_THREAD a_type_ptr signed_int_types[(int)ik_last]; #if MICROSOFT_EXTENSIONS_ALLOWED STATIC_THREAD a_type_ptr microsoft_sized_int_types[(int)ik_last]; STATIC_THREAD a_type_ptr microsoft_sized_signed_int_types[(int)ik_last]; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if FIXED_POINT_ALLOWED STATIC_THREAD a_type_ptr fixed_point_types[(int)fpp_last][/*is_unsigned*/2] [/*is_fract*/2][/*saturating*/2]; #endif /* FIXED_POINT_ALLOWED */ STATIC_THREAD a_type_ptr float_types[(int)fk_last]; #if C99_IL_EXTENSIONS_SUPPORTED STATIC_THREAD a_type_ptr complex_types[(int)fk_last]; STATIC_THREAD a_type_ptr imaginary_types[(int)fk_last]; #endif /* C99_IL_EXTENSIONS_SUPPORTED */ #define MAX_TRACKED_STRING_TYPE_LENGTH 80 STATIC_THREAD a_type_ptr (*string_types)[MAX_TRACKED_STRING_TYPE_LENGTH+1]; STATIC_THREAD a_type_ptr il_error_type; STATIC_THREAD a_type_ptr il_unknown_type; STATIC_THREAD a_type_ptr il_void_type; STATIC_THREAD a_type_ptr il_reflection_type; STATIC_THREAD a_type_ptr il_wchar_t_type; STATIC_THREAD a_type_ptr il_char8_t_type; STATIC_THREAD a_type_ptr il_char16_t_type; STATIC_THREAD a_type_ptr il_char32_t_type; STATIC_THREAD a_type_ptr il_bool_type; STATIC_THREAD a_type_ptr il_standard_nullptr_type; STATIC_THREAD a_type_ptr il_managed_nullptr_type; STATIC_THREAD a_type_ptr il_std_string_view; STATIC_THREAD a_type_ptr il_scalable_vector_count_type; STATIC_THREAD a_type_ptr il_modal_8bit_floating_point_type; STATIC_THREAD a_type_ptr il_float8e4m3_type; STATIC_THREAD a_type_ptr il_float8e5m2_type; /* Forward declare in_gnu_stmt_expression so that statements.h does not need to be included directly for this one function. */ #if GNU_EXTENSIONS_ALLOWED && CHECKING extern a_boolean in_gnu_stmt_expression(); #else /* !(GNU_EXTENSIONS_ALLOWED && CHECKING) */ #define in_gnu_stmt_expression() (FALSE) #endif /* GNU_EXTENSIONS_ALLOWED && CHECKING */ /* Conditionally close the "edg" namespace. */ END_EDG_NAMESPACE #endif /* !STANDALONE_UTILITY_PROGRAM */ /* Conditionally open the "edg" namespace. */ BEGIN_EDG_NAMESPACE #if DEBUG #if !STANDALONE_UTILITY_PROGRAM /* Number of times the based_types lists of types are searched for related types. */ STATIC_THREAD unsigned long num_get_based_type_calls; #endif /* !STANDALONE_UTILITY_PROGRAM */ #endif /* DEBUG */ /* Data structure used to save information about the last source sequence to file/line number conversion that was done so that subsequent conversions can be done more quickly. */ typedef struct a_source_sequence_cache_entry { a_seq_number first_seq_number; /* First sequence number for which the cached information is valid. */ a_seq_number last_seq_number; /* Last sequence number for which the cached information is valid. */ long line_offset; /* Value to be subtracted from a sequence number to convert it to a line number of the source file associated with this cached information. */ a_boolean physical_line; /* TRUE if the cached information reflects a physical line lookup. */ a_source_file_ptr source_file; /* Cached source file pointer. */ } a_source_sequence_cache_entry; STATIC_THREAD a_source_sequence_cache_entry seq_cache; #if !STANDALONE_UTILITY_PROGRAM STATIC_THREAD a_seq_number_lookup_entry_ptr curr_seq_number_lookup_entry; /* Pointer to the sequence number lookup entry for the source file segment currently being read. */ #endif /* !STANDALONE_UTILITY_PROGRAM */ STATIC_THREAD a_boolean okay_to_use_seq_number_lookup_table; /* TRUE if the sequence number lookup table is known to be in a valid state so that it can be used for sequence number to file/line conversions. The table cannot be used when the IL is being reread from an IL or PCH file. */ #define SEQ_NUMBER_LOOKUP_TABLE_INITIAL_ALLOCATION 1024 /* The initial number of elements in the sequence number lookup table. */ STATIC_THREAD unsigned long seq_number_lookup_table_size; /* The allocated size of the sequence number lookup table. */ STATIC_THREAD a_seq_number_lookup_entry_ptr *seq_number_lookup_table; /* Pointer to a dynamically allocated array of sequence number lookup entries. See the description of a_seq_number_lookup_entry and the routine add_seq_number_lookup_entry for more information. */ STATIC_THREAD a_source_file_ptr effective_primary_source_file; /* A pointer to the effective primary source file entry. This is usually the same as the primary source file, but is different for files that begin with #line directives. For more information see eff_primary_source_file. */ #if !STANDALONE_UTILITY_PROGRAM /* Hash table containing shareable constants (i.e., constants that can be reused when necessary, representing simple literal constants, so that for example there would only be one constant for the literal "0"). */ #define SIZE_SHAREABLE_CONSTANTS_TABLE 2039 /* Size of the table; should be about twice the expected number of entries for a big program, and must be prime. */ STATIC_THREAD a_constant_ptr *shareable_constants_table; /* Each entry in the table points to a linear linked list of constant entries. The function hash_constant is used to determine the table entry/list that corresponds to a given constant. The table is a trans-unit array that is dynamically allocated when the translation unit is initialized. */ #if DEBUG STATIC_THREAD unsigned long num_shareable_constants, num_func_shareable_constants, num_used_shareable_constant_buckets, num_searches_for_shareable_constants, num_compares_for_shareable_constants; #endif /* DEBUG */ /* Data struct used to do a fixup pass on based-type lists containing entries that should not escape to the back end. This class is declared (but not defined) in trans_unit.h. */ typedef struct a_based_type_fixup { a_based_type_fixup_ptr next; /* Next in the linked list of fixup entries; NULL if this is the last on the list. */ a_type_ptr base_type; /* The type whose based-type list should be scanned during the based-type-list fixup. */ } a_based_type_fixup; #if DEBUG STATIC_THREAD unsigned long num_based_type_fixups_allocated; #endif /* DEBUG */ #if DEBUG void db_template_arg_list(a_template_arg_ptr tap) /* Dump a list of template arguments, enclosed by angle brackets. */ { if (tap != NULL) { fputs("<", f_debug); do { if (is_type_templ_arg(tap)) { if (tap->variant.type == NULL) { fprintf(f_debug, ""); } else if (tap->variant.type->source_corresp.name == NULL) { db_abbreviated_type(tap->variant.type); } else { db_type_name(tap->variant.type); } /* if */ } else if (is_template_templ_arg(tap)) { if (tap->variant.templ.ptr == NULL) { fprintf(f_debug, ""); } else { db_template_name(tap->variant.templ.ptr); } /* if */ } else if (tap->is_array_bound_of_unknown_type) { fprintf(f_debug, "array-bound=%lu", (unsigned long)tap->variant.integer_value); } else if (is_nontype_templ_arg(tap)) { if (tap->arg_operand != NULL) { fprintf(f_debug, " "); } /* if */ db_constant(tap->variant.constant); } else { fprintf(f_debug, "[... placeholder] "); } /* if */ if (tap->is_pack_element) fprintf(f_debug, "+"); if (tap->pack_expansion_descr != NULL) fprintf(f_debug, "..."); if (tap->is_array_bound_of_unknown_type) { fprintf(f_debug, " array-bound ([%lu])", (unsigned long)tap->variant.integer_value); } else { a_template_param_coordinate_ptr coord; coord = coordinates_of_template_arg(tap); if (coord != NULL) { fprintf(f_debug, " #(%lu,%lu)", (unsigned long)(unsigned)coord->depth, (unsigned long)coord->position); } /* if */ } /* if */ tap = tap->next; if (tap != NULL) fputs(",", f_debug); } while (tap != NULL); fputs(">", f_debug); } /* if */ } /* db_template_arg_list */ void db_tap(a_template_arg_ptr tap) /* Shorthand for db_template_arg_list. */ { db_template_arg_list(tap); } /* db_tap */ void db_subst_pairs(a_subst_pairs_array const &pairs) /* Output descriptions of paired template parameter/argument lists. */ { for (size_t k = 0; k < pairs.length(); ++k) { fprintf(f_debug, "Pair #%zu:\n", k); db_tpp(pairs[k].params); fprintf(f_debug, "\n"); db_tap(pairs[k].args); fprintf(f_debug, "\n"); } /* for */ } /* db_subst_pairs */ void db_template_name(a_template_ptr tp) /* Dump the name of a template. */ { db_name(&tp->source_corresp); } /* db_template_name */ void db_type_name(a_type_ptr tp) /* Dump the name of a type. If it's a class generated on the basis of a template, dump the template arguments, too. */ { fprintf(f_debug, "%s", db_name_str(&tp->source_corresp, iek_type)); } /* db_type_name */ void db_name_full(a_source_correspondence *sc, an_il_entry_kind kind) /* Dump the name from a source correspondence (if any). This gets the entry kind and can provide full information, e.g., template arguments on template classes. */ { if (sc == NULL) { fputs("", f_debug); } else { fprintf(f_debug, "%s", db_name_str(sc, kind)); } /* if */ } /* db_name_full */ void db_name(a_source_correspondence *sc) /* Dump the name from a source correspondence (if any). This doesn't provide template arguments on template classes. */ { db_name_full(sc, iek_none); } /* db_name */ #endif /* DEBUG */ #endif /* !STANDALONE_UTILITY_PROGRAM */ void put_str_into_text_buffer(a_const_char *str, an_il_to_str_output_control_block_ptr octl) /* Output a string into the db_name_str buffer. */ { add_string_to_text_buffer((a_text_buffer_ptr)octl->text_buffer, str); } /* put_str_into_text_buffer */ #if DEBUG /* Buffer into which names are written for db_name_str */ STATIC_THREAD a_text_buffer_ptr db_name_str_buffer; #if !STANDALONE_UTILITY_PROGRAM /* Buffer used to generate output by db_qualifier_str. */ STATIC_THREAD a_text_buffer_ptr db_qualifiers_str_buffer; #endif /* !STANDALONE_UTILITY_PROGRAM */ char *db_name_str_full(a_source_correspondence *scp, an_il_entry_kind kind, a_boolean include_func_params) /* Return the name of an entity. The name string is in a buffer and should not be expected to stay around for a long time. kind is the kind of entity. The name includes the translation unit file name if the entity is not from the primary translation unit. If include_func_params is TRUE, include type information for function parameters. */ { an_il_to_str_output_control_block octl; /* Set up for use of form_name. */ clear_il_to_str_output_control_block(&octl); octl.output_str = put_str_into_text_buffer; if (db_name_str_buffer == NULL) { db_name_str_buffer = alloc_text_buffer(1000); } /* if */ reset_text_buffer(db_name_str_buffer); octl.text_buffer = db_name_str_buffer; octl.debug_output = TRUE; #if !STANDALONE_UTILITY_PROGRAM if (in_front_end) { /* Generate a translation unit name if this entity's symbol is not from the primary translation unit. */ a_const_char *trans_unit_name; trans_unit_name = db_symbol_trans_unit((a_symbol_ptr)scp->assoc_info); if (trans_unit_name != NULL) { add_char_to_text_buffer(db_name_str_buffer, '['); if (!in_secondary_trans_unit(scp)) { /* This entity was in a secondary translation unit, but has been copied to the primary IL. */ add_string_to_text_buffer(db_name_str_buffer, "copied from "); } /* if */ add_string_to_text_buffer(db_name_str_buffer, trans_unit_name); add_char_to_text_buffer(db_name_str_buffer, ']'); } /* if */ } /* if */ #endif /* !STANDALONE_UTILITY_PROGRAM */ /* Generate the name of this entity. */ form_name(scp, kind, &octl); if (include_func_params && kind == (an_il_entry_kind)iek_routine) { /* Add function parameter types. */ a_routine_ptr rout = (a_routine_ptr)scp; form_function_declarator(f_skip_typerefs(rout->type), &octl); #if DO_IL_LOWERING && IA64_ABI switch (rout->ctor_dtor_kind) { case cdk_complete: add_string_to_text_buffer(db_name_str_buffer, " [complete]"); break; case cdk_subobject: add_string_to_text_buffer(db_name_str_buffer, " [subobject]"); break; case cdk_deleting: add_string_to_text_buffer(db_name_str_buffer, " [deleting]"); break; case cdk_delegation: add_string_to_text_buffer(db_name_str_buffer, " [delegation]"); break; case cdk_none: break; default: unexpected_condition_str("bad ctor_dtor_kind"); } /* switch */ #endif /* DO_IL_LOWERING && IA64_ABI */ #if USE_X86_FUNCTION_MULTIVERSIONING if (has_gnu_routine_supp(rout)) { if (gnu_routine_supp(rout)->is_representative) { add_string_to_text_buffer(db_name_str_buffer, " [mv:representative]"); } else if (gnu_routine_supp(rout)->is_target_specific_version) { a_const_char *target_str = #if STANDALONE_UTILITY_PROGRAM "target-specific"; #else /* !STANDALONE_UTILITY_PROGRAM */ is_mv_default_routine(rout) ? "default" : target_specific_distinction(rout); #endif /* STANDALONE_UTILITY_PROGRAM */ add_string_to_text_buffer(db_name_str_buffer, " [mv:"); add_string_to_text_buffer(db_name_str_buffer, target_str); add_string_to_text_buffer(db_name_str_buffer, "]"); } /* if */ } /* if */ #endif /* USE_X86_FUNCTION_MULTIVERSIONING */ } /* if */ add_char_to_text_buffer(db_name_str_buffer, '\0'); return db_name_str_buffer->buffer; } /* db_name_str_full */ char *db_name_str(a_source_correspondence *scp, an_il_entry_kind kind) /* Return the name of an entity. The name string is in a buffer and should not be expected to stay around for a long time. kind is the kind of entity. The name includes the translation unit file name if the entity is not from the primary translation unit. */ { return db_name_str_full(scp, kind, /*include_func_params=*/TRUE); } /* db_name_str */ #endif /* DEBUG */ #if !STANDALONE_UTILITY_PROGRAM #if DEBUG void subst_fail_intercept(void) /* This function is called when type or expression substitution fails. It exists primarily to provide a target for a debugger breakpoint to ease the tracking of substitution errors. See also "record_suppressed_error" for expression contexts. */ { } /* subst_fail_intercept */ void db_scp(void *entity) /* Output a brief description of the given entity (which is assumed to start with a source correspondence). */ { a_source_correspondence_ptr scp = (a_source_correspondence_ptr)entity; a_symbol_ptr sym = (a_symbol_ptr)scp->assoc_info; a_line_number line; a_const_char *file_name, *full_name; a_boolean at_end_of_source; if (scp->assoc_info != NULL) { db_symbol_name(sym); if (symbol_is(sym, sk_variable)) { a_variable_ptr var = sym->variant.variable.ptr; if (var->template_info != NULL && var->template_info->template_arg_list != NULL) { db_template_arg_list(var->template_info->template_arg_list); } /* if */ } /* if */ fprintf(f_debug, " (%s)", symbol_kind_names[(int)sym->kind]); } else { db_name(scp); } /* if */ (void)conv_seq_to_file_and_line(scp->decl_position.seq, &file_name, &full_name, &line, &at_end_of_source); if (line != 0) { fprintf(f_debug, " in file %s (line %lu)\n", file_name, (unsigned long)line); } else { fprintf(f_debug, " (built-in; line %lu)\n", (unsigned long)line); } /* if */ } /* db_scp */ void db_entity_info(char *entry, an_il_entry_kind kind) /* Display the name of the indicated entity (one that begins with a source correspondence field and has kind "kind") along with some related information, such as its address and translation unit. */ { char *copy_addr = NULL; a_boolean has_defn = FALSE, show_defn_state = FALSE; if (entry == NULL) { fprintf(f_debug, "\n"); } else { #if MAINTAIN_ALLOCATION_SEQUENCE_NUMBER fprintf(f_debug, "(seq %lu) ", il_entry_prefix_of(entry).alloc_seq_number); #endif /* MAINTAIN_ALLOCATION_SEQUENCE_NUMBER */ fprintf(f_debug, "%s", il_entry_kind_names[(int)kind]); if (kind == iek_type) { fprintf(f_debug, " "); db_abbreviated_type((a_type_ptr)entry); } else if (source_corresp_for_il_entry(entry, kind) != NULL) { fprintf(f_debug, " %s", db_name_str((a_source_correspondence *)entry, kind)); } /* if */ fprintf(f_debug, "\n"); fprintf(f_debug, "address = %p", (a_void_ptr)entry); fprintf(f_debug, ", in %s trans unit", in_secondary_trans_unit(entry) ? "secondary" : "primary"); fprintf(f_debug, ", in %s scope", in_file_scope(entry) ? "file" : "function"); #if DO_IL_LOWERING fprintf(f_debug, ", %slowered", visited_yet(entry) ? "" : "un-"); #endif /* DO_IL_LOWERING */ if (kind == iek_type) { a_type_ptr type = (a_type_ptr)entry; if (is_immediate_class_type(type)) { show_defn_state = TRUE; has_defn = class_type_has_body(type); } else if (is_immediate_enum_type(type)) { show_defn_state = TRUE; has_defn = !is_incomplete_type(type); } /* if */ } else if (kind == iek_routine) { a_routine_ptr rout = (a_routine_ptr)entry; show_defn_state = TRUE; has_defn = (rout->function_def_number != NULL_function_def_number); } else if (kind == iek_variable) { a_variable_ptr var = (a_variable_ptr)entry; show_defn_state = TRUE; has_defn = (var->storage_class == (a_storage_class)sc_unspecified); } /* if */ if (show_defn_state) { fprintf(f_debug, ", %s definition", has_defn ? "has" : "no"); } /* if */ fprintf(f_debug, "\n"); if (in_front_end) { a_source_correspondence *scp = source_corresp_for_il_entry(entry, kind); a_trans_unit_corresp_ptr tucp = (scp == NULL) ? NULL : scp->trans_unit_corresp; if (tucp != NULL) { /* Display the correspondence information. */ fprintf(f_debug, "corresp = %p, canonical = %p, primary = %p\n", (a_void_ptr)tucp, (a_void_ptr)tucp->canonical, (a_void_ptr)tucp->primary); } /* if */ if (in_secondary_trans_unit(entry) && in_file_scope(entry)) { copy_addr = trans_unit_copy_address_of(entry); fprintf(f_debug, "copy address = %p", (a_void_ptr)copy_addr); /* Check for a second level copy address. */ if (copy_addr != NULL && in_secondary_trans_unit(copy_addr)) { copy_addr = trans_unit_copy_address_of(copy_addr); fprintf(f_debug, ", %p", (a_void_ptr)copy_addr); } /* if */ fprintf(f_debug, "\n"); } /* if */ } /* if */ } /* if */ } /* db_entity_info */ void db_entity_list(an_il_entity_list_entry *list) /* Output the summarized contents of the given IL entity list. */ { while (list != NULL) { db_entity_info(list->entity.ptr, list->entity.kind); list = list->next; if (list != NULL) { fprintf(f_debug, "\n\n"); } /* if */ } /* while */ } /* db_entity_list */ static void db_name_linkage(a_name_linkage_kind nlk) /* Dump the indicated name linkage kind. */ { fprintf(f_debug, "%s", name_linkage_kind_names[(int)nlk]); } /* db_name_linkage */ void db_abbreviated_type(a_type *tp) /* Dump a type in abbreviated form. This is particularly required for classes, structs, unions, which may contain fields that point to objects of their own type. */ { if (tp == NULL) { fputs("", f_debug); } else { switch (tp->kind) { case tk_class: fputs("class ", f_debug); goto print_name; case tk_struct: fputs("struct ", f_debug); goto print_name; case tk_union: fputs("union ", f_debug); print_name: db_type_name(tp); break; case tk_integer: if (is_immediate_enum_type(tp)) { fputs("enum ", f_debug); db_type_name(tp); /* Display the underlying integer kind if it's other than "int". */ if (tp->variant.integer.int_kind != (an_integer_kind)ik_int) { fprintf(f_debug, " (%s)", int_type_name(tp)); } /* if */ break; } /* if */ FALLTHROUGH default: db_type(tp); break; } /* switch */ } /* if */ } /* db_abbreviated_type */ void db_access_control(an_access_specifier as) /* Dump an access control specifier. */ { switch (as) { case as_public: fputs("public", f_debug); break; case as_protected: fputs("protected", f_debug); break; case as_private: fputs("private", f_debug); break; case as_inaccessible: fputs("inaccessible", f_debug); break; default: fputs("", f_debug); break; } /* switch */ } /* db_access_control */ #if MICROSOFT_EXTENSIONS_ALLOWED static void db_assembly_visibility_of_type(a_type_ptr type) /* Dump the assembly visibility specifier of the given type. */ { if (cli_or_cx_enabled) { switch (get_assembly_visibility_of(type)) { case av_none: /* Nothing to output. */ break; case av_public: fputs("public ", f_debug); break; case av_private: fputs("private ", f_debug); break; default: fputs(" ", f_debug); break; } /* switch */ } /* if */ } /* db_assembly_visibility_of_type */ #else /* !MICROSOFT_EXTENSIONS_ALLOWED */ #define db_assembly_visibility_of_type(type) /* Nothing */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ void db_field(a_field_ptr fp, int depth) /* Dump a field entry, for debug purposes. */ { int i; fputs("\n ", f_debug); if (depth > 0) for (i = depth; i > 0; --i) fputs(" ", f_debug); if (C_dialect == C_dialect_cplusplus) { db_access_control((an_access_specifier)fp->source_corresp.access); (void)fputc(' ', f_debug); } /* if */ fputs("field \"", f_debug); db_name_full(&fp->source_corresp, iek_field); fputs("\"", f_debug); if (fp->is_mutable) fputs(", mutable", f_debug); #if MICROSOFT_EXTENSIONS_ALLOWED if (fp->is_initonly) fputs(", initonly", f_debug); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (fp->is_optimized_empty_class) fputs(", opt", f_debug); fputs(", type = ", f_debug); db_abbreviated_type(fp->type); fprintf(f_debug, ", offset = %lu", (unsigned long)fp->offset); if (fp->is_bit_field) { fprintf(f_debug, "+%d, size = %d bit%s", (int)fp->offset_bit_remainder, (int)fp->bit_size, fp->bit_size == 1 ? "" : "s"); } /* if */ } /* db_field */ static void db_static_data_member(a_variable_ptr vp) /* Dump a static data member (a variable entry), for debug purposes. */ { fputs("\n ", f_debug); db_access_control((an_access_specifier)vp->source_corresp.access); fputs(" static data member \"", f_debug); db_name_full(&vp->source_corresp, iek_variable); fputs("\" (", f_debug); db_name_linkage((a_name_linkage_kind)vp->source_corresp.name_linkage); fprintf(f_debug, " linkage), sc_%s, type = ", db_storage_class_names[(int)vp->storage_class]); db_abbreviated_type(vp->type); } /* db_static_data_member */ static void db_member_function(a_routine_ptr rp) /* Dump a member function (a routine entry), for debug purposes. */ { fputs("\n ", f_debug); db_access_control((an_access_specifier)rp->source_corresp.access); if (!routine_type_is_nonstatic_member_function(rp->type)) { fputs(" static", f_debug); } /* if */ if (rp->is_virtual) { if (rp->pure_virtual) fputs(" pure", f_debug); fprintf(f_debug, " virtual (%d)", rp->number.virtual_function); } /* if */ fputs(" member function \"", f_debug); db_name_full(&rp->source_corresp, iek_routine); fputs("\" (", f_debug); db_name_linkage((a_name_linkage_kind)rp->source_corresp.name_linkage); fprintf(f_debug, " linkage)%s%s%s%s, sc_%s,\n type = ", (rp->is_consteval) ? ", consteval" : (rp->is_constexpr) ? ", constexpr" : "", (rp->is_inline) ? ", inline" : "", (rp->is_deleted) ? ", =delete" : "", (rp->is_inheriting_ctor) ? ", inheriting" : "", db_storage_class_names[(int)rp->storage_class]); db_abbreviated_type(rp->type); } /* db_member_function */ static void db_virtual_function_info(a_type_ptr tp, int depth) /* Dump the virtual_function_info_offset field of a class_type_supplement, for debug purposes. */ { a_class_type_supplement_ptr ctsp; int i = depth; if (tp->variant.class_struct_union.any_virtual_functions) { fputs("\n ", f_debug); for (; i > 0; --i) fputs(" ", f_debug); ctsp = tp->variant.class_struct_union.extra_info; fprintf(f_debug, "byte offset for virtual function table ptr = %lu", (unsigned long)ctsp->virtual_function_info_offset); if (ctsp->virtual_function_info_base_class != NULL) { fputs(", in ", f_debug); db_name_full( &ctsp->virtual_function_info_base_class->type->source_corresp, iek_type); } /* if */ } /* if */ } /* db_virtual_function_info */ static void db_virtual_base_class_ptr(a_base_class *bcp, int depth) /* Dump information on a virtual base class pointer (the pointer, not the base class itself), for debug purposes. */ { int i; fputs("\n ", f_debug); for (i = depth; i > 0; --i) fputs(" ", f_debug); fputs("[[ virtual ", f_debug); db_access_control(preferred_derivation_of(bcp)->access); fprintf(f_debug, " base class %s", bcp->type->source_corresp.name); #if !IA64_ABI fprintf(f_debug, " (pointer offset = %lu", (unsigned long)bcp->pointer_offset); if (bcp->pointer_base_class != NULL) { fprintf(f_debug, ", in %s", bcp->pointer_base_class->type->source_corresp.name); } /* if */ #endif /* IA64_ABI */ fputs(") ]]", f_debug); } /* db_virtual_base_class_ptr */ static void db_virtual_base_class(a_base_class *bcp, int depth); static a_base_class_derivation_ptr direct_virtual_derivation_of( a_base_class_ptr bcp); /* bcp is assumed to point to a base class for which direct is set to TRUE. If it is a virtual base class, return a pointer to the virtual derivation entry associated with its direct path; otherwise return a pointer to whatever derivation entry it points to. */ #define direct_derivation_of(bcp) \ ((bcp)->is_virtual ? direct_virtual_derivation_of(bcp) : \ (bcp)->derivation) static void db_direct_base_class(a_base_class *bcp, int depth) /* Dump a direct base class entry, for debug purposes. */ { a_type *tp = bcp->type; a_field *fp; int i; #if CFRONT_OBJECT_CODE_COMPATIBILITY a_boolean complete_subobject = bcp->complete_subobject; #endif /* CFRONT_OBJECT_CODE_COMPATIBILITY */ fputs("\n ", f_debug); for (i = depth; i > 0; --i) fputs(" ", f_debug); fputs("[[ ", f_debug); if (bcp->is_virtual) { fputs("virtual ", f_debug); } /* if */ db_access_control(direct_derivation_of(bcp)->access); fprintf(f_debug, " base class %s%s", tp->source_corresp.name, bcp->is_optimized_empty_base ? " (opt)" : ""); if (bcp->is_virtual) { #if !IA64_ABI fprintf(f_debug, " (pointer offset = %lu", (unsigned long)bcp->pointer_offset); if (bcp->pointer_base_class != NULL) { fprintf(f_debug, ", in %s", bcp->pointer_base_class->type->source_corresp.name); } /* if */ fputc(')', f_debug); #endif /* !IA64_ABI */ } else { #if CFRONT_OBJECT_CODE_COMPATIBILITY if (complete_subobject) fputs(" (complete subobj)", f_debug); #endif /* CFRONT_OBJECT_CODE_COMPATIBILITY */ fprintf(f_debug, " (offset = %lu)", (unsigned long)bcp->offset); for (bcp = tp->variant.class_struct_union.extra_info->base_classes; bcp != NULL; bcp = bcp->next) { if (bcp->direct && !bcp->is_virtual) { db_direct_base_class(bcp, depth+1); } /* if */ } /* for */ fp = tp->variant.class_struct_union.field_list; while (fp != NULL) { db_field(fp, depth+1); fp = fp->next; } /* while */ /* Put out the virtual base class pointers. */ for (bcp = tp->variant.class_struct_union.extra_info->base_classes; bcp != NULL; bcp = bcp->next) { if (bcp->direct && bcp->is_virtual) { db_direct_base_class(bcp, depth+1); } /* if */ } /* for */ db_virtual_function_info(tp, depth+1); #if CFRONT_OBJECT_CODE_COMPATIBILITY if (complete_subobject) { /* Put out the virtual base class data sections. */ for (bcp = tp->variant.class_struct_union.extra_info->base_classes; bcp != NULL; bcp = bcp->next) { if (bcp->direct && bcp->is_virtual && bcp->data_section_base_class == NULL) { db_virtual_base_class(bcp, depth+1); } /* if */ } /* for */ } /* if */ #endif /* CFRONT_OBJECT_CODE_COMPATIBILITY */ } /* if */ fputs(" ]]", f_debug); } /* db_direct_base_class */ static void db_indirect_base_class(a_base_class *bcp) /* Dump an indirect base class entry, for debug purposes. */ { a_derivation_step_ptr dsp; a_base_class_derivation_ptr bcdp; fputs("\n ", f_debug); db_type_name(bcp->type); fprintf(f_debug, ", at offset %lu", (unsigned long)bcp->offset); if (bcp->is_virtual) fputs(", virtual", f_debug); if (bcp->ambiguous) fputs(", ambig", f_debug); bcdp = bcp->derivation; if (bcdp == NULL) { fputs(", null derivation", f_debug); } else { fprintf(f_debug, ", path%s = ", bcdp->next != NULL ? "s" : ""); for (;;) { dsp = bcdp->path; if (dsp == NULL) { fputs("", f_debug); } else { for (; dsp != NULL; dsp = dsp->next) { fputs("==>", f_debug); if (dsp->base_class == NULL || dsp->base_class->type == NULL) { fputs("", f_debug); } else { db_type_name(dsp->base_class->type); } /* if */ } /* for */ } /* if */ if (bcp->is_virtual && bcdp->preferred && bcp->derivation->next != NULL) { fputs(" (pref'd)", f_debug); } /* if */ bcdp = bcdp->next; if (bcdp == NULL) break; fputs("; ", f_debug); } /* for */ } /* if */ } /* db_indirect_base_class */ static void db_virtual_base_class(a_base_class *bcp, int depth) /* Dump a virtual base class entry, for debug purposes. */ { a_type *tp = bcp->type; a_field *fp; int i; #if CFRONT_OBJECT_CODE_COMPATIBILITY a_boolean complete_subobject = bcp->complete_subobject; #endif /* CFRONT_OBJECT_CODE_COMPATIBILITY */ fputs("\n ", f_debug); for (i = depth; i > 0; --i) fputs(" ", f_debug); fprintf(f_debug, "[( virtual base class %s (offset = %lu", tp->source_corresp.name, (unsigned long)bcp->offset); #if CFRONT_OBJECT_CODE_COMPATIBILITY if (bcp->data_section_base_class != NULL) { fprintf(f_debug, ", in %s", bcp->data_section_base_class->type->source_corresp.name); } /* if */ #endif /* CFRONT_OBJECT_CODE_COMPATIBILITY */ fputc(')', f_debug); #if CFRONT_OBJECT_CODE_COMPATIBILITY if (bcp->data_section_base_class == NULL) { #endif /* CFRONT_OBJECT_CODE_COMPATIBILITY */ for (bcp = tp->variant.class_struct_union.extra_info->base_classes; bcp != NULL; bcp = bcp->next) { if (bcp->direct && !bcp->is_virtual) { db_direct_base_class(bcp, depth+1); } /* if */ } /* for */ fp = tp->variant.class_struct_union.field_list; while (fp != NULL) { db_field(fp, depth+1); fp = fp->next; } /* while */ for (bcp = tp->variant.class_struct_union.extra_info->base_classes; bcp != NULL; bcp = bcp->next) { if (bcp->direct && bcp->is_virtual) { db_direct_base_class(bcp, depth+1); } /* if */ } /* for */ db_virtual_function_info(tp, depth+1); #if CFRONT_OBJECT_CODE_COMPATIBILITY if (complete_subobject) { /* Put out the virtual base class data sections. */ for (bcp = tp->variant.class_struct_union.extra_info->base_classes; bcp != NULL; bcp = bcp->next) { if (bcp->direct && bcp->is_virtual && bcp->data_section_base_class == NULL) { db_virtual_base_class(bcp, depth+1); } /* if */ } /* for */ } /* if */ } /* if */ #endif /* CFRONT_OBJECT_CODE_COMPATIBILITY */ fputs(" )]", f_debug); } /* db_virtual_base_class */ static void db_using_decl(a_using_decl_ptr udp) /* Dump information on a using-decl entry, for debug purposes. */ { a_source_correspondence *sc; a_const_char *str; if (!udp->is_using_directive) { /* A using-declaration. */ if (udp->entity.kind == iek_base_class) { sc = &((a_base_class_ptr)udp->entity.ptr)->type->source_corresp; } else { sc = source_corresp_for_il_entry(udp->entity.ptr, (an_il_entry_kind)udp->entity.kind); } /* if */ check_assertion(sc != NULL); fputs("\n ", f_debug); if (udp->is_class_member) { /* A class member using-declaration. */ switch (udp->entity.kind) { case iek_variable: str = "static data member"; break; case iek_field: str = "field"; break; case iek_routine: str = "member function"; break; case iek_type: str = "member type"; break; case iek_constant: str = "member constant"; break; case iek_base_class: str = "member base"; break; case iek_template: str = "template"; break; default: str = NULL; break; } /* switch */ } else { /* A nonmember using declaration. */ switch (udp->entity.kind) { case iek_variable: str = "variable"; break; case iek_routine: str = "function"; break; case iek_type: str = "type"; break; case iek_constant: str = "constant"; break; case iek_template: str = "template"; break; default: str = NULL; break; } /* switch */ } /* if */ if (str == NULL) { fputs("", f_debug); } else { if (udp->is_class_member) { db_access_control(udp->access); fputc(' ', f_debug); } /* if */ fprintf(f_debug, " \"%s\" = %s ", sc->name, str); if (!udp->is_class_member && !scp_is_namespace_member(sc)) { fputs("::", f_debug); } /* if */ db_name_full(sc, (an_il_entry_kind)udp->entity.kind); if (udp->hidden) fprintf(f_debug, ", hidden"); if (udp->entity.kind == iek_routine) { fputs(",\n ", f_debug); db_type(((a_routine_ptr)udp->entity.ptr)->type); } /* if */ } /* if */ } /* if */ } /* db_using_decl */ void db_function_param_list(a_type_ptr tp) /* If tp is a routine type, dump the function parameters, for debug purposes. */ { a_param_type_ptr ptp; a_boolean comma_required = FALSE; tp = skip_typerefs(tp); if (tp->kind == (a_type_kind)tk_routine) { fputs("(", f_debug); for (ptp = tp->variant.routine.extra_info->param_type_list; ptp != NULL; ptp = ptp->next) { if (comma_required) fputs(", ", f_debug); #if MICROSOFT_EXTENSIONS_ALLOWED if (ptp->is_cli_param_array) fputs("... ", f_debug); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (has_name(ptp->type)) { db_type_name(ptp->type); } else { db_abbreviated_type(ptp->type); } /* if */ comma_required = TRUE; } /* for */ if (tp->variant.routine.extra_info->has_ellipsis) { if (comma_required) fputs(", ", f_debug); fputs("...", f_debug); } /* if */ fputs(")", f_debug); } /* if */ } /* db_function_param_list */ void db_ctor_init(a_constructor_init_ptr cip, size_t level) /* Dump information on a constructor initializer, for debug purposes. level is the amount of indentation. */ { a_dynamic_init_ptr dip = cip->initializer; db_indent(level); switch (cip->kind) { case cik_virtual_base_class: case cik_direct_base_class: { a_base_class_ptr bcp = cip->variant.base_class; fprintf(f_debug, "%s base ", bcp->is_virtual ? "virtual" : "direct"); db_name(&cip->variant.base_class->type->source_corresp); fputc('\n', f_debug); break; } case cik_field: fputs("field ", f_debug); db_name(&cip->variant.field->source_corresp); fputc('\n', f_debug); if (cip->use_field_initializer) { dip = cip->variant.field->initializer; } /* if */ break; case cik_delegation: fputs("delegate constructor", f_debug); break; default: unexpected_condition_str("Unexpected constructor init kind"); } /* switch */ db_dynamic_initializer(dip, level + 2); } /* db_ctor_init */ /*lint -esym(714,*db_cip)*/ void db_cip(a_constructor_init_ptr cip) /* A wrapper for db_ctor_init that uses no initial indentation. */ { db_ctor_init(cip, 0); } /* db_cip */ void db_ctor_init_list(a_constructor_init_ptr cip_list, size_t level) /* Dump information on all the constructor initializers in a list, for debug purposes. level is the amount of indentation. */ { db_indent(level); fputs("ctor init list:\n", f_debug); for (; cip_list != NULL; cip_list = cip_list->next) { db_ctor_init(cip_list, level + 2); } /* for */ } /* db_ctor_init_list */ /*lint -esym(714,*db_cip_list)*/ void db_cip_list(a_constructor_init_ptr cip_list) /* A wrapper for db_ctor_init_list that uses no initial indentation. */ { db_ctor_init_list(cip_list, 0); } /* db_cip_list */ static void db_add_qualifier_to_string(a_type_qualifier_set qualifier, a_type_qualifier_set qualifiers, a_const_char *name) /* If "qualifier" is set in "qualifiers" add "name" to the qualifier buffer. If "qualifier" is TQ_NONE, unconditionally add "name" */ { if ((qualifiers & qualifier) != 0 || qualifier == TQ_NONE) { /* Put a space between qualifiers. */ if (db_qualifiers_str_buffer->size != 0) { add_char_to_text_buffer(db_qualifiers_str_buffer, ' '); } /* if */ add_string_to_text_buffer(db_qualifiers_str_buffer, name); } /* if */ } /* db_add_qualifier_to_string */ char* db_qualifiers_str(a_type_qualifier_set qualifiers) /* Return a string containing the given qualifiers in human-readable form. */ { #if NAMED_ADDRESS_SPACES_ALLOWED a_named_address_space_id nas_id = named_address_space_from_qualifier_set(qualifiers); #endif /* NAMED_ADDRESS_SPACES_ALLOWED */ if (db_qualifiers_str_buffer == NULL) { db_qualifiers_str_buffer = alloc_text_buffer(128); } /* if */ reset_text_buffer(db_qualifiers_str_buffer); db_add_qualifier_to_string((a_type_qualifier_set)TQ_CONST, qualifiers, "const"); db_add_qualifier_to_string((a_type_qualifier_set)TQ_VOLATILE, qualifiers, "volatile"); db_add_qualifier_to_string((a_type_qualifier_set)TQ_RESTRICT, qualifiers, "restrict"); db_add_qualifier_to_string((a_type_qualifier_set)TQ_C11_ATOMIC, qualifiers, "_Atomic"); db_add_qualifier_to_string((a_type_qualifier_set)TQ_NULLABLE, qualifiers, "_Nullable"); db_add_qualifier_to_string((a_type_qualifier_set)TQ_NONNULL, qualifiers, "_Nonnull"); db_add_qualifier_to_string((a_type_qualifier_set)TQ_NULL_UNSPECIFIED, qualifiers, "_Null_unspecified"); #if MICROSOFT_EXTENSIONS_ALLOWED db_add_qualifier_to_string((a_type_qualifier_set)TQ_UNALIGNED, qualifiers, "unaligned"); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if NEAR_AND_FAR_ALLOWED db_add_qualifier_to_string((a_type_qualifier_set)TQ_NEAR, qualifiers, "near"); db_add_qualifier_to_string((a_type_qualifier_set)TQ_FAR, qualifiers, "far"); #endif /* NEAR_AND_FAR_ALLOWED */ #if UPC_EXTENSIONS_ALLOWED /* TQ_UPC_SHARED is handled by db_shared_block_size. */ db_add_qualifier_to_string((a_type_qualifier_set)TQ_UPC_STRICT, qualifiers, "strict"); db_add_qualifier_to_string((a_type_qualifier_set)TQ_UPC_RELAXED, qualifiers, "relaxed"); #endif /* UPC_EXTENSIONS_ALLOWED */ #if NAMED_ADDRESS_SPACES_ALLOWED if (nas_id != 0) { /* A named address space qualifier. */ db_add_qualifier_to_string((a_type_qualifier_set)TQ_NONE, qualifiers, named_address_spaces[nas_id].name); } /* if */ #endif /* NAMED_ADDRESS_SPACES_ALLOWED */ /* Terminate the buffer. */ add_char_to_text_buffer(db_qualifiers_str_buffer, '\0'); return db_qualifiers_str_buffer->buffer; } /* db_qualifiers_str */ static void db_qualifiers(a_type_qualifier_set qualifiers) /* Print the given qualifiers in human-readable form. */ { if (qualifiers != TQ_NONE) { fprintf(f_debug, "%s ", db_qualifiers_str(qualifiers)); } /* if */ } /* db_qualifiers */ #if MICROSOFT_EXTENSIONS_ALLOWED static void db_pointer_modifiers(a_pointer_modifier_set modifiers) /* Print the given pointer modifiers in human-readable form. */ { if ((modifiers & PM_SPTR) != 0) { fputs("signed ", f_debug); } /* if */ if ((modifiers & PM_UPTR) != 0) { fputs("unsigned ", f_debug); } /* if */ if ((modifiers & PM_PTR32) != 0) { fputs("32-bit ", f_debug); } /* if */ if ((modifiers & PM_PTR64) != 0) { fputs("64-bit ", f_debug); } /* if */ } /* db_pointer_modifiers */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if UPC_EXTENSIONS_ALLOWED static void db_shared_block_size(a_type_ptr tp) /* If the given type is UPC shared, print the corresponding qualifier, including the associated block size. */ { if (tp->variant.typeref.qualifiers & TQ_UPC_SHARED) { if (tp->variant.typeref.extra_info->upc_block_size == UPC_BLOCK_SIZE_NONE) { fprintf(f_debug, "shared "); } else if (tp->variant.typeref.extra_info->upc_block_size == UPC_BLOCK_SIZE_BLOCK) { fprintf(f_debug, "shared [*] "); } else { fprintf(f_debug, "shared [%ld] ", tp->variant.typeref.extra_info->upc_block_size); } /* if */ } /* if */ } /* db_shared_block_size */ #endif /* UPC_EXTENSIONS_ALLOWED */ static void db_exception_spec(an_exception_specification_ptr esp) /* Dump the given exception specification (which may be NULL), for debug purposes. */ { if (esp == NULL || esp->throw_any) { if (exceptions_enabled) fputs("throws any", f_debug); } else if (esp->indeterminate) { fputs(" ", f_debug); } else if (esp->is_noexcept) { fputs("noexcept", f_debug); if (esp->arg_cached) { fputs(" ", f_debug); } else if (esp->copy_from_prototype) { fputs(" ", f_debug); } else if (esp->variant.noexcept_arg != NULL) { fputs(" ", f_debug); db_constant(esp->variant.noexcept_arg); } /* if */ } else if (esp->variant.exception_specification_type_list == NULL) { fputs("throws none", f_debug); } else { an_exception_specification_type_ptr estp; estp = esp->variant.exception_specification_type_list; fputs("throws (", f_debug); db_abbreviated_type(estp->type); for (estp = estp->next; estp != NULL; estp = estp->next) { fputs(", ", f_debug); db_abbreviated_type(estp->type); } /* for */ fputs(")", f_debug); } /* if */ } /* db_exception_spec */ void db_param_type_list(a_param_type_ptr ptp, /* Defaulted: */ a_boolean comma_required) /* Output a comma-separated list of parameters described by ptp. If comma_required is TRUE (it is defaulted to FALSE) output ", " first. */ { while (ptp != NULL) { if (comma_required) fputs(", ", f_debug); #if MICROSOFT_EXTENSIONS_ALLOWED if (ptp->is_cli_param_array) fputs("... ", f_debug); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (ptp->qualifiers != TQ_NONE && remove_qualifiers_from_param_types) { a_type_qualifier_set qualifiers = ptp->qualifiers; fputs("[", f_debug); if (qualifiers & TQ_CONST) { fputs("const", f_debug); qualifiers &= ~TQ_CONST; if (qualifiers != TQ_NONE) fputs(" ", f_debug); } /* if */ if (qualifiers & TQ_VOLATILE) { fputs("volatile", f_debug); qualifiers &= ~TQ_VOLATILE; if (qualifiers != TQ_NONE) fputs(" ", f_debug); } /* if */ if ((qualifiers & TQ_RESTRICT) && !keep_restrict_in_signatures) { fputs("restrict", f_debug); } /* if */ fputs("] ", f_debug); } /* if */ db_abbreviated_type(ptp->type); if (ptp->is_parameter_pack) fputs(" ...", f_debug); if (ptp->has_default_arg) { an_expr_node_ptr expr = ptp->default_arg_expr; fputs(" (= ", f_debug); if (expr == NULL) { /* Can be NULL for default arguments based on template parameters. */ fputs("", f_debug); } else { switch (expr->kind) { case enk_constant: db_constant(node_constant(expr)); break; case enk_variable: db_name_full(&node_variable(expr)->source_corresp, iek_variable); break; case enk_error: fputs("", f_debug); break; default: fputs("", f_debug); break; } /* switch */ } /* if */ fputs(")", f_debug); } /* if */ comma_required = TRUE; ptp = ptp->next; } /* while */ } /* db_param_type_list */ void db_type(a_type *tp) /* Dump the contents of the indicated type entry, for debug purposes. */ { a_field_ptr fp; a_class_type_supplement_ptr ctsp; a_routine_type_supplement_ptr rtsp; a_boolean comma_required; if (tp == NULL) { fputs("", f_debug); } else { switch (tp->kind) { case tk_error: fputs("", f_debug); break; case tk_unknown: fputs("", f_debug); break; case tk_void: fputs("void", f_debug); break; case tk_reflection: fputs("reflection", f_debug); break; case tk_integer: if (tp->variant.integer.wchar_t_type) { fputs("wchar_t", f_debug); } else if (tp->variant.integer.char8_t_type) { fputs("char8_t", f_debug); } else if (tp->variant.integer.char16_t_type) { fputs("char16_t", f_debug); } else if (tp->variant.integer.char32_t_type) { fputs("char32_t", f_debug); } else if (tp->variant.integer.bool_type) { fputs("bool", f_debug); } else { fprintf(f_debug, "%s", int_type_name(tp)); if (tp->variant.integer.enum_type) { fputs(" ", f_debug); db_assembly_visibility_of_type(tp); fputs("enum", f_debug); if (tp->variant.integer.is_scoped_enum) { fputs(" class", f_debug); } /* if */ } /* if */ } /* if */ break; #if FIXED_POINT_ALLOWED case tk_fixed_point: { a_fixed_point_precision prec = tp->variant.fixed_point.precision; fprintf(f_debug, "fixed-point%s%s%s%s", tp->variant.fixed_point.saturating ? " _Sat" : "", tp->variant.fixed_point.is_unsigned ? " unsigned" : "", (prec == (a_fixed_point_precision)fpp_short) ? " short" : (prec == (a_fixed_point_precision)fpp_default) ? "" : (prec == (a_fixed_point_precision)fpp_long) ? " long" : " *ERROR*", tp->variant.fixed_point.is_fract_type ? " _Fract" : " _Accum"); } break; #endif /* FIXED_POINT_ALLOWED */ #if C99_IL_EXTENSIONS_SUPPORTED case tk_complex: case tk_imaginary: fprintf(f_debug, "%s", float_kind_name(tp->variant.float_kind, /*use_C_form=*/TRUE)); fprintf(f_debug, "%s", tp->kind == (a_type_kind)tk_complex ? " _Complex" : " _Imaginary"); break; #endif /* C99_IL_EXTENSIONS_SUPPORTED */ case tk_float: fprintf(f_debug, "%s", float_kind_name(tp->variant.float_kind, C_mode())); break; case tk_pointer: if (tp->variant.pointer.is_reference) { if (tp->variant.pointer.is_rvalue_reference) { fputs("rvalue ref to ", f_debug); #if MICROSOFT_EXTENSIONS_ALLOWED } else if (tp->variant.pointer.is_handle) { fputs("tracking ref to ", f_debug); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else { fputs("ref to ", f_debug); } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED } else if (tp->variant.pointer.is_handle) { fputs("handle to ", f_debug); } else if (tp->variant.pointer.is_interior_ptr) { fputs("interior_ptr to ", f_debug); } else if (tp->variant.pointer.is_pin_ptr) { fputs("pin_ptr to ", f_debug); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else { #if MICROSOFT_EXTENSIONS_ALLOWED if (tp->variant.pointer.base_variable != NULL) { fputs("based(", f_debug); db_name_full(&tp->variant.pointer.base_variable->source_corresp, iek_variable); fputs(") ", f_debug); } /* if */ db_pointer_modifiers(tp->variant.pointer.modifiers); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ fputs("ptr to ", f_debug); } /* if */ db_abbreviated_type(tp->variant.pointer.type); break; case tk_array: fputs("array [", f_debug); if (tp->variant.array.is_static) { fputs("static ", f_debug); } /* if */ if (tp->variant.array.is_vla) { if (tp->variant.array.has_assoc_vla_dimension) { fputs("**EXPR**", f_debug); } else { fputc('*', f_debug); } /* if */ } else if (tp->variant.array.is_variable_size_array) { fputs("**EXPR**", f_debug); } else if (tp->variant.array.is_template_dependent_size_array) { if (tp->variant.array.variant.element_count_constant != NULL) { db_constant(tp->variant.array.variant.element_count_constant); } else { fprintf(f_debug, "**DEPENDENT**"); } /* if */ } else { fprintf(f_debug, "%lu", (unsigned long)tp->variant.array.variant.number_of_elements); #if UPC_EXTENSIONS_ALLOWED if (tp->variant.array.is_threads_dimension) { fprintf(f_debug, "*THREADS"); } /* if */ #endif /* UPC_EXTENSIONS_ALLOWED */ } /* if */ fputs("] of ", f_debug); db_abbreviated_type(tp->variant.array.element_type); break; case tk_struct: case tk_union: case tk_class: db_assembly_visibility_of_type(tp); ctsp = class_type_supp(tp); #if MICROSOFT_EXTENSIONS_ALLOWED if (ctsp != NULL) { fputs(cli_class_type_kind_is(tp, cctk_ref) ? "ref " : cli_class_type_kind_is(tp, cctk_value) ? "value " : cli_class_type_kind_is(tp, cctk_interface) ? "interface " : "", f_debug); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ fputs(tp->kind == (a_type_kind)tk_union ? "union " : tp->kind == (a_type_kind)tk_struct ? "struct " : "class ", f_debug); #if MICROSOFT_EXTENSIONS_ALLOWED if (ctsp != NULL) { a_type_kind orig_type_kind = ctsp->orig_type_kind; if (orig_type_kind != tp->kind) { fputs("[orig: ", f_debug); switch (orig_type_kind) { case tk_struct: fputs("struct", f_debug); break; case tk_union: fputs("union", f_debug); break; case tk_class: fputs("class", f_debug); break; default: fputs("***BAD KIND***", f_debug); } /* switch */ fputs("] ", f_debug); } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ db_type_name(tp); if (class_type_supp(tp) == NULL) { fputs(" (missing class type supplement)", f_debug); } else if (!class_type_has_body(tp)) { fputs(" (undefined)", f_debug); } else { a_base_class_ptr bcp = NULL; a_boolean any_virtual_base_classes = FALSE; a_boolean any_indirect_base_classes = FALSE; if (tp->variant.class_struct_union.abstract) { fputs(" (abstract)", f_debug); } /* if */ if (ctsp != NULL && ctsp->anonymous_union_kind != (an_anonymous_union_kind)auk_none) { fprintf(f_debug, " (anonymous, %s)", ctsp->anonymous_union_kind == (an_anonymous_union_kind)auk_field ? "field" : "var"); } /* if */ fputs(" {", f_debug); if (ctsp != NULL) bcp = ctsp->base_classes; for (; bcp != NULL; bcp = bcp->next) { if (bcp->direct) { if (!bcp->is_virtual) db_direct_base_class(bcp, 0); } else { any_indirect_base_classes = TRUE; } /* if */ if (bcp->is_virtual) any_virtual_base_classes = TRUE; } /* for */ fp = tp->variant.class_struct_union.field_list; for (; fp != NULL; fp = fp->next) db_field(fp, 0); if (any_virtual_base_classes) { if (ctsp != NULL) bcp = ctsp->base_classes; for (; bcp != NULL; bcp = bcp->next) { if (bcp->is_virtual) { #if !CFRONT_OBJECT_CODE_COMPATIBILITY if (!bcp->direct) continue; #endif /* !CFRONT_OBJECT_CODE_COMPATIBILITY */ db_virtual_base_class_ptr(bcp, 0); } /* if */ } /* for */ } /* if */ if (ctsp != NULL && !scope_is_null_or_placeholder(ctsp->assoc_scope)) { a_variable_ptr vp = ctsp->assoc_scope->variables; a_routine_ptr rp = ctsp->assoc_scope->routines; a_using_decl_ptr udecp = ctsp->assoc_scope->using_declarations; a_using_decl_ptr udirp = ctsp->assoc_scope->using_directives; db_virtual_function_info(tp, /*nesting_depth=*/0); if (any_virtual_base_classes) { fputs("\n collected virtual base classes:", f_debug); for (bcp = ctsp->base_classes; bcp != NULL; bcp = bcp->next) { if (bcp->is_virtual) db_virtual_base_class(bcp, 0); } /* for */ } /* if */ if (any_indirect_base_classes) { fputs("\n indirect base classes:", f_debug); for (bcp = ctsp->base_classes; bcp != NULL; bcp = bcp->next) { if (!bcp->direct) db_indirect_base_class(bcp); } /* for */ } /* if */ if (vp != NULL) { fputs("\n static data members:", f_debug); for (; vp != NULL; vp = vp->next) db_static_data_member(vp); } /* if */ if (rp != NULL) { fprintf( f_debug, "\n member functions (highest virtual func number = %d):", ctsp->highest_virtual_function_number); for (; rp != NULL; rp = rp->next) db_member_function(rp); } /* if */ if (udecp != NULL) { fputs("\n using-declarations:", f_debug); for (; udecp != NULL; udecp = udecp->next) { db_using_decl(udecp); } /* if */ } /* if */ if (udirp != NULL) { fputs("\n using-directives:", f_debug); for (; udirp != NULL; udirp = udirp->next) { db_using_decl(udirp); } /* if */ } /* if */ } /* if */ fputc('\n', f_debug); if (ctsp != NULL) { db_all_virtual_function_override_lists(tp); } /* if */ fprintf(f_debug, "} : size = %lu, alignment = %d", (unsigned long)tp->size, tp->alignment); if (any_virtual_base_classes) { fprintf(f_debug, "; w/o virtuals: size = %lu, alignment = %d", (unsigned long)ctsp->size_without_virtual_base_classes, ctsp->alignment_without_virtual_base_classes); } /* if */ } break; case tk_routine: rtsp = tp->variant.routine.extra_info; #if MICROSOFT_EXTENSIONS_ALLOWED if (rtsp->calling_convention != (a_calling_convention)cc_default) { fprintf(f_debug, "%s ", calling_convention_names[(int)rtsp->calling_convention]); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (rtsp->routine_name_linkage != (a_name_linkage_kind)nlk_none) { fputs("[", f_debug); db_name_linkage((a_name_linkage_kind)rtsp->routine_name_linkage); fputs("] ", f_debug); } /* if */ fputs("function", f_debug); if (rtsp->assoc_routine != NULL) { fprintf(f_debug, " %s", db_name_str_full(&rtsp->assoc_routine->source_corresp, iek_routine, /*include_func_params=*/FALSE)); } /* if */ fputs("(", f_debug); if (!rtsp->prototyped) { fputs("", f_debug); } /* if */ if (rtsp->this_class != NULL) { /* Display the type of *this (without qualifications). */ fputs("this: ", f_debug); db_type_name(rtsp->this_class); comma_required = TRUE; } else { /* No comma should be displayed next since we haven't emitted the first parameter yet. */ comma_required = FALSE; } /* if */ db_param_type_list(rtsp->param_type_list, comma_required); if (rtsp->has_ellipsis) { if (comma_required) fputs(", ", f_debug); fputs("...", f_debug); } /* if */ fputs(") ", f_debug); db_qualifiers(rtsp->qualifiers | rtsp->this_qualifiers); fputs("returning ", f_debug); db_abbreviated_type(tp->variant.routine.return_type); if (exc_spec_in_func_type) { fputs(" ", f_debug); db_exception_spec(tp->variant.routine.extra_info ->exception_specification); } /* if */ break; case tk_typeref: if (typeref_is_qualified(tp)) { db_qualifiers(tp->variant.typeref.qualifiers); #if UPC_EXTENSIONS_ALLOWED db_shared_block_size(tp); #endif /* UPC_EXTENSIONS_ALLOWED */ } else { fputs("typeref ", f_debug); if (has_name(tp)) { fputs("\"", f_debug); db_name_full(&tp->source_corresp, iek_type); fputs("\" ", f_debug); } /* if */ if (is_typeref_kind(tp, trk_is_decltype) || is_typeref_kind(tp, trk_is_splice)) { fputs(is_typeref_kind(tp, trk_is_splice) ? "[:" : "decltype(", f_debug); an_il_to_str_output_control_block octl; clear_il_to_str_output_control_block(&octl); octl.output_str = put_str_to_f_debug; octl.debug_output = TRUE; db_abbr_expr(decltype_arg(tp), &octl); fputs(is_typeref_kind(tp, trk_is_splice) ? ":] " : ") ", f_debug); } /* if */ if (is_typeref_kind(tp, trk_pack_index)) { db_abbreviated_type(typeref_supp(tp)->operator_type_arg); fputs("...[", f_debug); an_il_to_str_output_control_block octl; clear_il_to_str_output_control_block(&octl); octl.output_str = put_str_to_f_debug; octl.debug_output = TRUE; db_abbr_expr(decltype_arg(tp), &octl); fputs("] ", f_debug); } /* if */ if (typeref_is_type_transforming_intrinsic(tp)) { fputs(type_transforming_intrinsic_name(tp->variant.typeref.kind), f_debug); fputs(" ", f_debug); } /* if */ #if GNU_EXTENSIONS_ALLOWED if (is_typeref_kind(tp, trk_is_typeof_with_expression) || is_typeref_kind(tp, trk_is_typeof_with_type_operand)) { fputs("__typeof__ ", f_debug); } /* if */ #endif /* GNU_EXTENSIONS_ALLOWED */ #if GNU_EXTENSIONS_ALLOWED || MICROSOFT_EXTENSIONS_ALLOWED if (tp->alignment_set_explicitly) { fprintf(f_debug, "aligned(%d) ", tp->alignment); } /* if */ #endif /* GNU_EXTENSIONS_ALLOWED || MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ db_abbreviated_type(tp->variant.typeref.type); break; case tk_ptr_to_member: #if MICROSOFT_EXTENSIONS_ALLOWED db_pointer_modifiers(tp->variant.ptr_to_member.modifiers); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ fputs("ptr-to-member of ", f_debug); db_abbreviated_type(tp->variant.ptr_to_member.class_of_which_a_member); fputs(" of type ", f_debug); db_abbreviated_type(tp->variant.ptr_to_member.type); break; case tk_template_param: fputs("template-param", f_debug); if (tp->variant.template_param.kind == (a_template_param_type_kind)tptk_unknown) { fputs(" ", f_debug); } else { if (tp->variant.template_param.kind == (a_template_param_type_kind)tptk_param) { if (tp->variant.template_param.extra_info->coordinates.depth == AUTO_TYPE_NESTING_DEPTH) { if (tp->variant.template_param.extra_info->coordinates.position == PLAIN_AUTO_TYPE_POS_NUMBER) { fprintf(f_debug, " auto"); } else if (tp->variant.template_param.extra_info ->coordinates.position == DECLTYPE_AUTO_POS_NUMBER) { fprintf(f_debug, " decltype(auto)"); } else { fprintf(f_debug, " INVALID AUTO PLACEHOLDER"); } /* if */ } else if (tp->variant.template_param.extra_info ->coordinates.depth == CLASS_TEMPLATE_PLACEHOLDER_NESTING_DEPTH) { fprintf(f_debug, " placeholder %s", tp->source_corresp.name); } else { fprintf(f_debug, "#(%lu,%lu) ", (unsigned long)(unsigned)tp->variant. template_param.extra_info->coordinates.depth, (unsigned long)tp->variant. template_param.extra_info->coordinates.position); db_name(&tp->source_corresp); } /* if */ } else { fputc(' ', f_debug); db_name(&tp->source_corresp); } /* if */ } /* if */ break; #if GNU_VECTOR_TYPES_ALLOWED case tk_vector: fputs("vector of ", f_debug); db_abbreviated_type(tp->variant.vector.element_type); fputs(" (size = ", f_debug); if (tp->variant.vector.size_constant != NULL) { db_constant(tp->variant.vector.size_constant); } else { fprintf(f_debug, "%lu", (unsigned long)tp->size); } /* if */ fputs(" )", f_debug); break; case tk_scalable_vector: fputs("scalable vector of ", f_debug); db_abbreviated_type(tp->variant.scalable_vector.element_type); fprintf(f_debug, " (tuple elements = %u)", tp->variant.scalable_vector.tuple_elements); break; case tk_scalable_vector_count: fputs("scalable vector count", f_debug); break; case tk_mfp8: fputs("modal 8-bit floating-point type", f_debug); break; case tk_float8e4m3: fputs("OFP8 E4M3 8-bit floating-point type", f_debug); break; case tk_float8e5m2: fputs("OFP8 E5M2 8-bit floating-point type", f_debug); break; #endif /* GNU_VECTOR_TYPES_ALLOWED */ case tk_nullptr: if (tp->incomplete) { fputs("managed nullptr type", f_debug); } else { fputs("std::nullptr_t", f_debug); } /* if */ break; default: fputs("", f_debug); } /* switch */ } /* if */ } /* db_type */ void put_str_to_f_debug(a_const_char *str, ARG_UNUSED an_il_to_str_output_control_block_ptr octl) /* Output the indicated string to f_debug. This is used as an output routine when using the il_to_str routines. */ { fputs(str, f_debug); } /* put_str_to_f_debug */ void db_subobject_path(a_subobject_path *path) /* Output a subobject path for debug purposes. */ { for (; path != NULL; path = path->next) { if (path->is_offset) { fprintf(f_debug, "[%ld]", (long)path->variant.ptr_offset); } else if (path->is_base_class) { fprintf(f_debug, "base "); db_type_name(path->variant.base_class->type); } else { fprintf(f_debug, "."); db_name_full(&path->variant.field->source_corresp, iek_field); } /* if */ if (path->next != NULL) { fprintf(f_debug, "->"); } /* if */ } /* for */ } /* db_subobject_path */ void db_constant(a_constant *cp) /* Dump the contents of the indicated constant, for debug purposes. */ { if (cp == NULL) { fputs("", f_debug); } else { an_il_to_str_output_control_block octl; #if MICROSOFT_EXTENSIONS_ALLOWED if (cp->is_literal_field) fputs("literal ", f_debug); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Set up for use of form_constant. */ clear_il_to_str_output_control_block(&octl); octl.output_str = put_str_to_f_debug; octl.gen_pcc_code = (C_dialect == C_dialect_pcc); octl.debug_output = TRUE; /* If this is a template parameter, output its coordinates. */ if (constant_is(cp, ck_template_param)) { if (tpck_is(cp, tpck_param)) { fprintf(f_debug, "template-param#(%lu,%lu) ", (unsigned long)(unsigned) cp->variant.template_param.variant.coordinates.depth, (unsigned long)cp->variant.template_param.variant.coordinates.position); } else if (tpck_is(cp, tpck_expression)) { fprintf(f_debug, "tpck_expression "); } else if (tpck_is(cp, tpck_dependent_constant)) { fprintf(f_debug, "tpck_dependent_constant "); } else if (tpck_is(cp, tpck_concat_string_literals)) { fprintf(f_debug, "tpck_concat_string_literals "); } /* if */ } /* if */ if (constant_is(cp, ck_aggregate) && !(cp->explicit_cast_applied || cp->is_compound_literal)) { /* Emit the type name before elements of the aggregate (e.g., A{1, 2}) if an explicit cast won't be added by form_constant.*/ db_type_name(cp->type); } /* if */ /* Output the constant. */ form_constant(cp, /*need_parens=*/FALSE, &octl); if (is_enum_constant(cp) && has_name(cp)) { /* Display the value of the enum constant, too. */ fputs(" (= ", f_debug); form_integer_constant(cp, /*suppress_cast=*/TRUE, /*need_parens=*/FALSE, &octl); fputc(')', f_debug); } /* if */ } /* if */ } /* db_constant */ static void db_variable_without_type(a_variable_ptr var_ptr) /* Dump the contents of the indicated variable for debug purposes. */ { fputs("name = ", f_debug); db_name_full(&var_ptr->source_corresp, iek_variable); if (var_ptr->template_info != NULL && var_ptr->template_info->template_arg_list != NULL) { db_template_arg_list(var_ptr->template_info->template_arg_list); } /* if */ if (var_ptr->is_this_parameter) fputs(" (this)", f_debug); } /* db_variable_without_type */ void db_variable(a_variable_ptr var_ptr) /* Dump the contents of the indicated variable and its type for debug purposes. */ { db_variable_without_type(var_ptr); fputs(", type = ", f_debug); db_abbreviated_type(var_ptr->type); fputs("\n", f_debug); } /* db_variable */ void db_expr_node(an_expr_node_ptr node, size_t level) /* Dump the contents of the indicated expression node for debug purposes. */ { an_expr_node_ptr operand; a_constant_ptr const_ptr; a_new_delete_supplement_ptr ndsp; a_throw_supplement_ptr tsp; db_indent(level); if (node == NULL) { fprintf(f_debug, "\n"); goto done; } /* if */ if (node->is_pack_expansion) fprintf(f_debug, "[pack expansion] "); if (node->is_lvalue) fprintf(f_debug, "[lvalue] "); if (node->is_xvalue) fprintf(f_debug, "[xvalue] "); switch ((int)node->kind) { case enk_operation: if (node->variant.operation.returns_lvalue_instead_of_usual_rvalue) { fprintf(f_debug, "[C++] "); } /* if */ if (node->variant.operation.eval_right_to_left) { fprintf(f_debug, "[r-to-l] "); } /* if */ if (node->variant.operation.eval_left_to_right) { fprintf(f_debug, "[l-to-r] "); } /* if */ fprintf(f_debug, "operator: %s", db_operator_names[(int)node->variant.operation.kind]); fputs(", result type: ", f_debug); db_abbreviated_type(node->type); fputs("\n", f_debug); operand = node->variant.operation.operands; while (operand != NULL) { db_expr_node(operand, level + 2); operand = operand->next; } /* while */ break; case enk_constant: const_ptr = node_constant(node); if (const_ptr == NULL) { fprintf(f_debug, "constant: \n"); break; } /* if */ if (!has_name(const_ptr)) { fputs("constant: value = ", f_debug); } else { fprintf(f_debug, "constant (%s): value = ", const_ptr->source_corresp.name); } /* if */ db_constant(const_ptr); fputs("\n", f_debug); if (const_ptr->expr != NULL) { db_expr_node(const_ptr->expr, level + 2); } /* if */ break; case enk_variable: fputs("variable: ", f_debug); db_variable_without_type(node_variable(node)); fputs(", node type: ", f_debug); db_abbreviated_type(node->type); fputs("\n", f_debug); break; case enk_routine: fprintf(f_debug, "routine: %s\n", node_routine(node) == NULL ? "" : node_routine(node)->source_corresp.name != NULL ? node_routine(node)->source_corresp.name : ""); break; case enk_field: fprintf(f_debug, "field "); db_name_full(&node_field(node)->source_corresp, iek_field); fputs("\n", f_debug); break; case enk_temp_init: fprintf(f_debug, "temp init, result type: "); db_abbreviated_type(node->type); fputs(", ", f_debug); db_dynamic_initializer(node->variant.init.dynamic_init, level+2); break; case enk_new_delete: ndsp = node->variant.new_delete; fprintf(f_debug, "%s: routine = %s, type = ", ndsp->is_new ? "new" : "delete", (ndsp->routine != NULL) ? ndsp->routine->source_corresp.name : "(null)"); db_abbreviated_type(ndsp->type); fputs("\n", f_debug); for (operand = ndsp->arg; operand != NULL; operand = operand->next) { db_expr_node(operand, level + 2); } /* for */ if (ndsp->dynamic_init != NULL) { db_indent(level); fprintf(f_debug, "dynamic_init: "); db_dynamic_initializer(ndsp->dynamic_init, level + 2); } /* if */ break; case enk_lambda: fprintf(f_debug, "lambda\n"); break; #if MICROSOFT_EXTENSIONS_ALLOWED case enk_gcnew: { a_gcnew_supplement_ptr gsp = node->variant.gcnew_info; fprintf(f_debug, "gcnew: type = "); db_abbreviated_type(gsp->type); fputs("\n", f_debug); if (gsp->cli_array_dimension_lengths != NULL) { db_indent(level + 2); fprintf(f_debug, "dimension lengths:\n"); for (operand = gsp->cli_array_dimension_lengths; operand != NULL; operand = operand->next) { db_expr_node(operand, level + 4); } /* for */ } /* if */ if (gsp->dynamic_init != NULL) { db_indent(level + 2); fprintf(f_debug, "dynamic_init: "); db_dynamic_initializer(gsp->dynamic_init, level + 4); } /* if */ } break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case enk_throw: fputs("throw: ", f_debug); tsp = node->variant.throw_info; if (tsp == NULL) { fputs("rethrow\n", f_debug); } else { fprintf(f_debug, "type = "); db_abbreviated_type(tsp->type); fprintf(f_debug, ", dynamic_init = "); if (tsp->dynamic_init == NULL) { fprintf(f_debug, "\n"); } else { db_dynamic_initializer(tsp->dynamic_init, level + 2); } /* if */ } /* if */ break; case enk_condition: fputs("condition: ", f_debug); db_scope(node->variant.condition->scope); fputs(", ", f_debug); if (node->variant.condition->dynamic_init == NULL) { fputs("", f_debug); } else { a_variable_ptr vp = node->variant.condition->dynamic_init->variable; if (vp == NULL) { fputs("", f_debug); } else { db_name_full(&vp->source_corresp, iek_variable); } /* if */ fputs(" = ", f_debug); db_dynamic_initializer(node->variant.condition->dynamic_init, level+2); } /* if */ fputs("\n", f_debug); break; case enk_object_lifetime: fputs("object lifetime:\n", f_debug); db_expr_node(node->variant.object_lifetime.expr, level + 2); break; case enk_typeid: { an_expr_node_ptr opnds = node->variant.typeid_info.type_with_opt_expr; fputs("typeid: type = ", f_debug); if (opnds == NULL) { fputs("", f_debug); } else if (!node_is(opnds, enk_type_operand)) { fputs("", f_debug); } else { db_abbreviated_type(opnds->variant.type_operand.type); if (opnds->next == NULL) { fputc('\n', f_debug); } else { fputs(", expr =\n", f_debug); db_expr_node(opnds->next, level + 2); } /* if */ } /* if */ } break; case enk_alignof: fputs("alignof: ", f_debug); goto sizeof_cases; case enk_datasizeof: fputs("__datasizeof: ", f_debug); goto sizeof_cases; case enk_sizeof: fputs("sizeof: ", f_debug); sizeof_cases: if (node->variant.sizeof_info.is_type) { fputs("type = ", f_debug); db_abbreviated_type(node->variant.sizeof_info.variant.type); fputc('\n', f_debug); } else { fprintf(f_debug, "expr =\n"); db_expr_node(node->variant.sizeof_info.variant.expr, level + 2); } /* if */ break; case enk_sizeof_pack: fputs("sizeof pack: ", f_debug); if (node->variant.sizeof_pack.is_template_template) { fputs("template = ", f_debug); db_template_name(node->variant.sizeof_pack.variant.templ); fputc('\n', f_debug); } else if (node->variant.sizeof_pack.is_type) { fputs("type = ", f_debug); db_abbreviated_type(node->variant.sizeof_pack.variant.type); fputc('\n', f_debug); } else { fprintf(f_debug, "expr =\n"); db_expr_node(node->variant.sizeof_pack.variant.expr, level + 2); } /* if */ break; case enk_address_of_ellipsis: fputs("address of ellipsis\n", f_debug); break; case enk_statement: fputs("statement\n", f_debug); break; case enk_reuse_value: fputs("reuse value, dynamic init with ", f_debug); db_dynamic_initializer(node->variant.reused_value_init, level+2); break; #if DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING /* Nodes generated by IL lowering for partial lowering of exception handling features. */ case enk_lowered_eh_construct: fputs("lowered eh: ", f_debug); switch (node->variant.lowered_eh.kind) { case leck_caught_object_address: fputs("caught object address\n", f_debug); break; case leck_thrown_object_address: fputs("thrown object address\n", f_debug); break; case leck_unreachable_cleanup_state: fprintf(f_debug, "(unreachable) "); FALLTHROUGH case leck_cleanup_state: #if GENERATE_EH_TABLES fprintf(f_debug, "cleanup state, region number = %ld\n", (long)node->variant.lowered_eh.variant.cleanup_region_number); #else /* !GENERATE_EH_TABLES */ fputs("cleanup state\n", f_debug); db_dynamic_initializer(node->variant.lowered_eh.variant.cleanup_ptr, level+2); #endif /* GENERATE_EH_TABLES */ break; case leck_function_prologue: fputs("function prologue\n", f_debug); break; case leck_function_epilogue: fputs("function epilogue\n", f_debug); break; case leck_catch_epilogue: fputs("catch epilogue\n", f_debug); break; case leck_try_epilogue: fputs("try epilogue\n", f_debug); break; case leck_exception_caught: fputs("exception caught\n", f_debug); break; case leck_exception_started: fputs("exception started\n", f_debug); break; case leck_internal_try: fputs("internal try\n", f_debug); break; default: fputs("\n", f_debug); } /* switch */ break; #endif /* DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING */ #if DO_IL_LOWERING && ABI_CHANGES_FOR_COVARIANT_VIRTUAL_FUNC_RETURN case enk_result_of_overriding_function: /* Node generated as part of the body of an entry function used as a wrapper for a call of an overriding virtual function with a covariant return type. */ fputs("result of overriding function\n", f_debug); break; #endif /* DO_IL_LOWERING && ABI_CHANGES_FOR_COVARIANT_VIRTUAL_FUNC_RETURN */ #if VLA_DEALLOCATIONS_IN_IL case enk_vla_dealloc: fprintf(f_debug, "vla deallocation: "); if (node->variant.vla_variable == NULL) { fputs("\n", f_debug); } else { db_name_full(&node->variant.vla_variable->source_corresp, iek_variable); fputs("\n", f_debug); } /* if */ break; #endif /* VLA_DEALLOCATIONS_IN_IL */ case enk_type_operand: fprintf(f_debug, "type_operand: type = "); db_abbreviated_type(type_operand_type(node)); fputs("\n", f_debug); break; case enk_builtin_operation: fprintf(f_debug, "builtin operation: %s", builtin_operation_names[node->variant.builtin_operation.kind]); fputs(", result type: ", f_debug); db_abbreviated_type(node->type); fputs("\n", f_debug); operand = node->variant.builtin_operation.operands; while (operand != NULL) { db_expr_node(operand, level + 2); operand = operand->next; } /* while */ break; case enk_param_ref: if (node->variant.param_ref.param_num == 0) { /* A zero parameter number indicates "this". */ fprintf(f_debug, "param_ref: this, type = "); } else { fprintf(f_debug, "param_ref: param_num = %u, levels_up = %u, type = ", node->variant.param_ref.param_num, node->variant.param_ref.levels_up); } /* if */ db_abbreviated_type(node->type); fputs("\n", f_debug); break; case enk_braced_init_list: fputs("braced-init-list:\n", f_debug); operand = node->variant.braced_init_list; while (operand != NULL) { db_expr_node(operand, level + 2); operand = operand->next; } /* while */ break; case enk_c11_generic: fputs("c11-generic:\n", f_debug); operand = node->variant.c11_generic.operands; while (operand != NULL) { if (operand == node->variant.c11_generic.result) { db_indent(level); fprintf(f_debug, " =\n"); } /* if */ db_expr_node(operand, level + 2); operand = operand->next; } /* while */ break; #if BUILTIN_FUNCTIONS_ENABLED case enk_builtin_choose_expr: fputs("builtin-choose-expr:\n", f_debug); fputs(node->variant.builtin_choose_expr.choose_first ? "choose first\n" : "choose second\n", f_debug); operand = node->variant.builtin_choose_expr.operands; while (operand != NULL) { db_expr_node(operand, level + 2); operand = operand->next; } /* while */ break; #endif /* BUILTIN_FUNCTIONS_ENABLED */ case enk_await: case enk_yield: fputs(node->kind == (an_expr_node_kind)enk_await ? "await:\n" : "yield:\n", f_debug); db_indent(level); fprintf(f_debug, " =\n"); db_expr_node(node->variant.await_info.operand, level + 2); break; case enk_fold: fprintf(f_debug, "fold-expression, %s, ", node->variant.fold.left_associative ? "left-assoc, " : "right-assoc, "); operand = node->variant.fold.operands; if (operand == NULL) { fputs("MISSING OPERANDS", f_debug); } else if (operand->next == NULL) { fputs("unary", f_debug); } else { fputs("binary", f_debug); operand = operand->next; if (operand->next != NULL) { fputs("EXTRANEOUS OPNDS", f_debug); } /* if */ } /* if */ fputs("\n", f_debug); db_indent(level); fprintf(f_debug, "%s\n", token_names[node->variant.fold.operator_token]); operand = node->variant.fold.operands; for (; operand != NULL; operand = operand->next) { db_expr_node(operand, level + 2); } /* for */ break; case enk_initializer: fputs("initializer\n", f_debug); db_dynamic_initializer(node->variant.initializer.dyn_init, level+2); break; case enk_concept_id: fputs("concept-id ", f_debug); db_name_full(&node->variant.concept_id.concept_template->source_corresp, iek_template); if (node->variant.concept_id.args != NULL) { db_template_arg_list(node->variant.concept_id.args); fputs("\n", f_debug); } else { fputs("\n", f_debug); } /* if */ break; case enk_requires: fputs("requires-expr\n", f_debug); { an_expr_node_ptr req = node->variant.requires_expr.requirements; for (; req != NULL; req = req->next) { db_expr_node(req, level+2); } /* for */ } break; case enk_compound_req: fputs("compound-req\n", f_debug); db_expr_node(node->variant.compound_req.expr_and_constraint, level+2); if (node->variant.compound_req.expr_and_constraint->next != NULL) { db_indent(level); fputs(" ->", f_debug); db_expr_node(node->variant.compound_req.expr_and_constraint->next, level+6); } /* if */ break; case enk_nested_req: fputs("nested-req\n", f_debug); db_expr_node(node->variant.nested_req.constraint, level+2); break; case enk_const_eval_deferred: fputs("const-eval-deferred\n", f_debug); db_expr_node(node->variant.const_eval_deferred.wrapped, level+2); break; case enk_pack_index: fputs("pack-index-expr\n", f_debug); db_indent(level); fputs(" =\n", f_debug); db_expr_node(node->variant.pack_index.expr, level + 2); db_indent(level); fputs(" =\n", f_debug); db_expr_node(node->variant.pack_index.index_expr, level + 2); break; case enk_error: fputs("error node\n", f_debug); break; default: fputs("\n", f_debug); } /* switch */ done:; } /* db_expr_node */ void db_expression(an_expr_node_ptr node) /* Dump debug information on an expression node. */ { fputs("*** start of expression ***\n", f_debug); db_expr_node(node, 0); fputs("*** end of expression ***\n", f_debug); } /* db_expression */ void db_expr_range(ARG_UNUSED an_expr_node_ptr node) /* Dump the source expression range from the expression node if it is available. */ { #if EXTRA_SOURCE_POSITIONS_IN_IL if (node->expr_range.start.seq != 0) { fprintf(f_debug, ", at %lu/%lu", (unsigned long)node->expr_range.start.seq, (unsigned long)node->expr_range.start.column); if (node->expr_range.end.seq != 0) { fprintf(f_debug, " -- %lu/%lu", (unsigned long)node->expr_range.end.seq, (unsigned long)node->expr_range.end.column); } /* if */ } /* if */ #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* db_expr_range */ static void db_static_initializer(a_constant_ptr con) /* Dump debug information on a constant that is the initial value in a dynamic initialization entry or the static initial value of a variable. */ { if (con->kind == (a_constant_repr_kind)ck_aggregate) { fputs("{ ", f_debug); con = con->variant.aggregate.first_constant; for (; con != NULL; con = con->next) { db_static_initializer(con); if (con->next != NULL) fputs(", ", f_debug); } /* for */ fputs(" }", f_debug); } else if (con->kind == (a_constant_repr_kind)ck_init_repeat) { fprintf(f_debug, "%lu repetitions of: ", (unsigned long)con->variant.init_repeat.count); db_static_initializer(con->variant.init_repeat.constant); } else { db_constant(con); } /* if */ } /* db_static_initializer */ static void db_destructor(a_dynamic_init_ptr dip) /* Dump debug information on the destructor part of a dynamic initialization entry. */ { a_routine_ptr dtor = dip->destructor; #if DO_IL_LOWERING if (dip->is_vla_deallocation) { fprintf(f_debug, "VLA deallocation"); } else #endif /* DO_IL_LOWERING */ /* Do not insert code here. */ { fprintf(f_debug, "%sdtor: ", dip->destruction_is_for_partially_constructed_aggregate ? "EH-" : ""); if (dtor != NULL) { db_name_full(&dtor->source_corresp, iek_routine); } else { fputs("", f_debug); } /* if */ } /* if */ } /* db_destructor */ static void db_constructor_initializer(a_dynamic_init_ptr dip, size_t level) /* Dump debug information on a dynamic initialization entry of type dik_constructor. */ { an_expr_node_ptr arg; db_indent(level); fputs("ctor: ", f_debug); if (dip->variant.constructor.value_initialization) { fputs("(value initialization) ", f_debug); } /* if */ if (dip->variant.constructor.ptr == NULL) { fputs("", f_debug); } else { db_name_full(&dip->variant.constructor.ptr->source_corresp, iek_routine); } /* if */ if (dip->destructor != NULL) { fputs("; ", f_debug); db_destructor(dip); } /* if */ fputs("\n", f_debug); if ((arg = dip->variant.constructor.args) != NULL) { db_indent(level + 2); fputs("ctor args =\n", f_debug); for (; arg != NULL; arg = arg->next) { db_expr_node(arg, level+4); } /* if */ } /* if */ } /* db_constructor_initializer */ #if 0 static void db_dynamic_init_kind(a_dynamic_init_kind kind) /* Dump a string identifying a dynamic-init kind, for debug purposes. */ { char *s; switch (kind) { case dik_none: s = "dik_none"; break; case dik_zero: s = "dik_zero"; break; case dik_constant: s = "dik_constant"; break; case dik_expression: s = "dik_expression"; break; case dik_class_result_via_ctor: s = "dik_class_result_via_ctor"; break; case dik_constructor: s = "dik_constructor"; break; case dik_nonconstant_aggregate: s = "dik_nonconstant_aggregate"; break; case dik_bitwise_copy: s = "dik_bitwise_copy"; break; default: s = "**BAD DYNAMIC INIT KIND"; } /* switch */ fputs(s, f_debug); } /* db_dynamic_init_kind */ #endif /* 0 */ static void db_constant_repr_kind(a_constant_repr_kind kind) /* Dump a string identifying a constant-representation kind, for debug purposes. */ { a_const_char *s; switch (kind) { case ck_error: s = "ck_error"; break; case ck_integer: s = "ck_integer"; break; #if FIXED_POINT_ALLOWED case ck_fixed_point: s = "ck_fixed_point"; break; #endif /* FIXED_POINT_ALLOWED */ case ck_string: s = "ck_string"; break; case ck_float: s = "ck_float"; break; case ck_address: s = "ck_address"; break; case ck_ptr_to_member: s = "ck_ptr_to_member"; break; #if GNU_EXTENSIONS_ALLOWED case ck_label_difference: s = "ck_label_difference"; break; #endif /* GNU_EXTENSIONS_ALLOWED */ #if DO_IL_LOWERING && GENERATE_EH_TABLES && !DO_FULL_PORTABLE_EH_LOWERING case ck_stack_offset: s = "ck_stack_offset"; break; #endif /* DO_IL_LOWERING && ... */ case ck_dynamic_init: s = "ck_dynamic_init"; break; case ck_aggregate: s = "ck_aggregate"; break; case ck_init_repeat: s = "ck_init_repeat"; break; case ck_template_param: s = "ck_template_param"; break; case ck_designator: s = "ck_designator"; break; case ck_reflection: s = "ck_reflection"; break; #if UPC_EXTENSIONS_ALLOWED case ck_upc_threads: s = "ck_upc_threads"; break; case ck_upc_mythread: s = "ck_upc_mythread"; break; #endif /* UPC_EXTENSIONS_ALLOWED */ case ck_void: s = "ck_void"; break; default: s = "**BAD CONSTANT KIND"; } /* switch */ fputs(s, f_debug); } /* db_constant_repr_kind */ static void db_nonconstant_aggregate(a_constant_ptr con, size_t level) /* Dump debug information on a dynamic initialization entry of kind dik_nonconstant_aggregate. */ { for (; con != NULL; con = con->next) { db_indent(level); db_constant_repr_kind(con->kind); if (con->type != NULL) { fputs(" (", f_debug); db_abbreviated_type(con->type); fputs(")", f_debug); } /* if */ fputs(": ", f_debug); if (con->kind == (a_constant_repr_kind)ck_dynamic_init) { a_dynamic_init_ptr dip = con->variant.dynamic_init.ptr; db_dynamic_initializer(dip, level+2); } else { if (con->kind == (a_constant_repr_kind)ck_aggregate) { (void)fputc('\n', f_debug); db_nonconstant_aggregate(con->variant.aggregate.first_constant, level + 2); } else if (con->kind == (a_constant_repr_kind)ck_init_repeat) { fprintf(f_debug, "%lu repetitions of\n", (unsigned long)con->variant.init_repeat.count); db_nonconstant_aggregate(con->variant.init_repeat.constant, level + 2); } else { db_constant(con); (void)fputc('\n', f_debug); } /* if */ } /* if */ } /* for */ } /* db_nonconstant_aggregate */ static void db_lambda_initializer(a_dynamic_init_ptr dip, size_t level) /* Dump debug information on a dynamic initialization entry of kind dik_lambda. */ { a_lambda_ptr lambda; a_lambda_capture_ptr cap; a_constant_ptr cp, field_con; check_assertion(dip->kind == (a_dynamic_init_kind)dik_lambda); lambda = dip->variant.constant.lambda; cap = lambda->capture_list; cp = dip->variant.constant.ptr; field_con = cp->variant.aggregate.first_constant; /* Iterate over the captured fields. */ for (; cap != NULL && field_con != NULL; cap = cap->next, field_con = field_con->next) { db_indent(level); if (cap->is_init_capture) { fputs(cap->closure_field->source_corresp.name, f_debug); } else if (!cap->is_indirect_init_capture) { if (cap->captured.variable != NULL && !cap->captured.variable->is_this_parameter) { fputs(cap->captured.variable->source_corresp.name, f_debug); } else { fputs("this", f_debug); } /* if */ } else { fputs("", f_debug); } /* if */ fputs(" = ", f_debug); if (cap->is_init_capture) { db_dynamic_initializer(cap->captured.initializer, level + 2); } else if (field_con->kind == (a_constant_repr_kind)ck_dynamic_init) { a_dynamic_init_ptr sub_dip = field_con->variant.dynamic_init.ptr; fputs("(dynamic-init) ", f_debug); db_dynamic_initializer(sub_dip, level + 2); if (sub_dip->kind == (a_dynamic_init_kind)dik_bitwise_copy && sub_dip->variant.bitwise_copy.source == NULL && !cap->is_indirect_init_capture && cap->captured.variable != NULL) { /* Give more information on the implicit source. */ db_indent(level + 2); fputs("implicit source = ", f_debug); db_variable(cap->captured.variable); } } else { db_constant(field_con); fputs("\n", f_debug); } /* if */ } /* for */ } /* db_lambda_initializer */ void db_dynamic_initializer(a_dynamic_init_ptr dip, size_t level) /* Dump a dynamic initializer entry for debug purposes. */ { /* Avoid problems on call with missing second argument from debugger: */ if (level > 80) level = 0; if (dip == NULL) { db_indent(level); fputs("\n", f_debug); goto done; } /* if */ if (dip->variable != NULL) { fputs("variable: \"", f_debug); db_name_full(&dip->variable->source_corresp, iek_variable); fputs("\", ", f_debug); } /* if */ switch (dip->kind) { case dik_constant: db_indent(level); db_static_initializer(dip->variant.constant.ptr); if (dip->destructor != NULL) { fputs("; ", f_debug); db_destructor(dip); } /* if */ (void)fputc('\n', f_debug); break; case dik_expression: fputs("expression:\n", f_debug); db_expr_node(dip->variant.expression, level); goto destructor_on_next_line; case dik_class_result_via_ctor: fputs("result class via ctor:\n", f_debug); db_expr_node(dip->variant.expression, level); goto destructor_on_next_line; case dik_nonconstant_aggregate: fputs("nonconstant aggregate:\n", f_debug); db_nonconstant_aggregate(dip->variant.constant.ptr-> variant.aggregate.first_constant, level); destructor_on_next_line: if (dip->destructor != NULL) { db_indent(level); db_destructor(dip); (void)fputc('\n', f_debug); } /* if */ break; case dik_lambda: fputs("lambda:\n", f_debug); db_lambda_initializer(dip, level); goto destructor_on_next_line; case dik_constructor: db_constructor_initializer(dip, level); break; case dik_bitwise_copy: fputs(", source = ", f_debug); if (dip->variant.bitwise_copy.source == NULL) { fputs("implicit", f_debug); } else { db_expr_node(dip->variant.bitwise_copy.source, level); } /* if */ goto destructor_on_this_line; case dik_none: fputs("", f_debug); goto destructor_on_this_line; case dik_zero: fputs("", f_debug); destructor_on_this_line: if (dip->destructor != NULL) { fputs(", ", f_debug); db_destructor(dip); } /* if */ (void)fputc('\n', f_debug); break; default: fputs("***BAD DYNAMIC INIT KIND***\n", f_debug); break; } /* switch */ done:; } /* db_dynamic_initializer */ void db_dip(a_dynamic_init_ptr dip) /* Convenience function to call db_dynamic_initializer with no indentation. */ { db_dynamic_initializer(dip, 0); } /* db_dip */ void db_initializer(a_variable_ptr var, size_t level) /* Dump the initializer of a variable for debug purposes. */ { if (var->init_kind != (an_init_kind)initk_none) { db_indent(level); if (var->init_kind == (an_init_kind)initk_function_local) { fprintf(f_debug, "local static initialization\n"); } else if (var->init_kind == (an_init_kind)initk_binding) { fprintf(f_debug, "binding: "); (void)fputc('\n', f_debug); db_expr_node(var->initializer.bound_expr, 2); } else if (var->init_kind == (an_init_kind)initk_static) { fprintf(f_debug, "static init: "); db_static_initializer(var->initializer.constant); (void)fputc('\n', f_debug); } else if (var->init_kind == (an_init_kind)initk_zero) { fprintf(f_debug, "zero init\n"); } else { check_assertion(var->init_kind == initk_dynamic); fprintf(f_debug, "dynamic init: "); db_dynamic_initializer(var->initializer.dynamic, level + 2); } /* if */ } /* if */ } /* db_initializer */ void db_statement_kind(a_statement_kind kind) /* Dump a statement kind, for debug purposes. */ { a_const_char *s; switch (kind) { case stmk_empty: s = "empty"; break; case stmk_expr: s = "expr"; break; case stmk_if: s = "if"; break; case stmk_if_consteval: s = "if consteval"; break; case stmk_if_not_consteval: s = "if not consteval"; break; case stmk_constexpr_if: s = "constexpr if"; break; case stmk_while: s = "while"; break; case stmk_goto: s = "goto"; break; case stmk_label: s = "label"; break; case stmk_return: s = "return"; break; case stmk_coroutine: s = "coroutine"; break; case stmk_coroutine_return: s = "coroutine return"; break; case stmk_block: s = "block"; break; case stmk_end_test_while: s = "end-test-while"; break; case stmk_for: s = "for"; break; case stmk_range_based_for: s = "range-based-for"; break; #if MICROSOFT_EXTENSIONS_ALLOWED case stmk_for_each: s = "for each"; break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case stmk_switch_case: s = "switch-case"; break; case stmk_switch: s = "switch"; break; case stmk_init: s = "init"; break; case stmk_asm: s = "asm"; break; #if ASM_FUNCTION_ALLOWED case stmk_asm_func_body: s = "asm-func-body"; break; #endif /* ASM_FUNCTION_ALLOWED */ case stmk_try_block: s = "try-block"; break; #if MICROSOFT_EXTENSIONS_ALLOWED case stmk_microsoft_try: s = "microsoft-try"; break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case stmk_decl: s = "decl"; break; case stmk_set_vla_size: s = "set-vla-size"; break; case stmk_vla_decl: s = "vla-decl"; break; #if GNU_EXTENSIONS_ALLOWED case stmk_assigned_goto: s = "assigned goto"; break; #endif /* GNU_EXTENSIONS_ALLOWED */ #if UPC_EXTENSIONS_ALLOWED case stmk_upc_notify: s = "upc_notify"; break; case stmk_upc_wait: s = "upc_wait"; break; case stmk_upc_barrier: s = "upc_barrier"; break; case stmk_upc_fence: s = "upc_fence"; break; case stmk_upc_forall: s = "upc_forall"; break; #endif /* UPC_EXTENSIONS_ALLOWED */ case stmk_stmt_expr_result: s = "stmt-expr-result"; break; default: s = ""; break; } /* switch */ fputs(s, f_debug); } /* db_statement_kind */ void db_expr_summary(an_expr_node_ptr node) /* Dump some summary information about an expression node. Called by routines dumping other structures to which the node belongs. */ { if (node != NULL) { if (node->kind == (an_expr_node_kind)enk_object_lifetime) { /* For an object lifetime expression, display the underlying expression. */ node = node->variant.object_lifetime.expr; } /* if */ switch (node->kind) { case enk_operation: fprintf(f_debug, " (operator %s)", db_operator_names[node->variant.operation.kind]); break; case enk_throw: fprintf(f_debug, " (throw)"); break; case enk_new_delete: fprintf(f_debug, " (%s)", node->variant.new_delete->is_new ? "new" : "delete"); break; case enk_lambda: fprintf(f_debug, " (lambda)"); break; #if MICROSOFT_EXTENSIONS_ALLOWED case enk_gcnew: fprintf(f_debug, " (gcnew)"); break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING case enk_lowered_eh_construct: fprintf(f_debug, " (lowered eh construct)"); break; #endif /* DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING */ #if DO_IL_LOWERING && ABI_CHANGES_FOR_COVARIANT_VIRTUAL_FUNC_RETURN case enk_result_of_overriding_function: fprintf(f_debug, " (result of overriding function)"); break; #endif /* DO_IL_LOWERING && ABI_CHANGES_FOR_COVARIANT_VIRTUAL_FUNC_RETURN */ case enk_condition: fprintf(f_debug, " (condition)"); break; #if VLA_DEALLOCATIONS_IN_IL case enk_vla_dealloc: fprintf(f_debug, " (vla deallocation)"); break; #endif /* VLA_DEALLOCATIONS_IN_IL */ case enk_statement: fprintf(f_debug, " (gnu statement expr)"); break; default:; } /* switch */ } /* if */ } /* db_expr_summary */ void db_statement(a_statement_ptr sp) /* Dump a statement, for debug purposes. */ { if (sp != NULL) { db_statement_kind(sp->kind); fputs("-stmt", f_debug); switch (sp->kind) { case stmk_block: if (sp->variant.block.extra_info->assoc_scope != NULL) { fputs(" [", f_debug); db_scope(sp->variant.block.extra_info->assoc_scope); fputs("]", f_debug); } /* if */ break; case stmk_expr: db_expr_summary(sp->expr); break; case stmk_label: case stmk_goto: if (sp->variant.label.ptr != NULL && sp->variant.label.ptr->source_corresp.name != NULL) { fputs(" \"", f_debug); db_name(&sp->variant.label.ptr->source_corresp); fputc('"', f_debug); } else { fprintf(f_debug, " <%p>", (void *)sp->variant.label.ptr); } /* if */ break; case stmk_switch_case: { a_switch_case_entry *scep = sp->variant.switch_case.extra_info; if (scep->case_value == NULL) { fputs(" (default)", f_debug); } else { fputs(" ", f_debug); db_constant(scep->case_value); #if GNU_EXTENSIONS_ALLOWED if (scep->range_end) { fputs(" ... ", f_debug); db_constant(scep->range_end); } /* if */ #endif /* GNU_EXTENSIONS_ALLOWED */ } /* if */ } break; case stmk_stmt_expr_result: if (sp->expr != NULL) { db_expr_summary(sp->expr); } else { a_dynamic_init_ptr dip = sp->variant.stmt_expr_result.dynamic_init; fputs(" ", f_debug); db_dip(dip); } /* if */ break; default:; } /* switch */ fprintf(f_debug, ", at %lu/%lu", (unsigned long)sp->position.seq, (unsigned long)sp->position.column); #if EXTRA_SOURCE_POSITIONS_IN_IL if (sp->end_position.seq != 0) { fprintf(f_debug, " -- %lu/%lu", (unsigned long)sp->end_position.seq, (unsigned long)sp->end_position.column); } /* if */ #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ fputc('\n', f_debug); } /* if */ } /* db_statement */ void db_statement_list(a_statement_ptr sp, size_t indent, a_const_char *str, int how_deep) /* Dump a list of statements, for debug purposes. sp is the head of the list. indent is the number of spaces to put out for indentation. str is a string which is to be put out after the indentation and before the first statement. how_deep is how many levels of recursion to go before terminating the dump (this one counts as the first). */ { a_handler_ptr hp; if (how_deep > 0) { for (; sp != NULL; sp = sp->next) { db_indent(indent); fputs(str, f_debug); db_statement(sp); switch (sp->kind) { case stmk_block: db_statement_list(sp->variant.block.statements, indent+2, "", how_deep-1); break; case stmk_if: case stmk_if_consteval: case stmk_if_not_consteval: case stmk_constexpr_if: { a_statement_ptr then_statement, else_statement; if (sp->kind == (a_statement_kind)stmk_constexpr_if) { then_statement = sp->variant.constexpr_if->then_statement; else_statement = sp->variant.constexpr_if->else_statement; } else { then_statement = sp->variant.if_stmt.then_statement; else_statement = sp->variant.if_stmt.else_statement; } /* if */ if (how_deep > 1) { if (then_statement == NULL) { db_indent(indent + 2); fprintf(f_debug, "then \n"); } else { db_statement_list(then_statement, indent+2, "then ", how_deep-1); } /* if */ if (else_statement != NULL) { db_statement_list(else_statement, indent+2, "else ", how_deep-1); } /* if */ } /* if */ } break; case stmk_for: db_statement_list(sp->variant.for_loop.statement, indent+2, "", how_deep-1); break; case stmk_range_based_for: db_statement_list(sp->variant.range_based_for_loop.statement, indent+2, "", how_deep-1); break; #if MICROSOFT_EXTENSIONS_ALLOWED case stmk_for_each: db_statement_list(sp->variant.for_each_loop.statement, indent+2, "", how_deep-1); break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case stmk_while: case stmk_end_test_while: db_statement_list(sp->variant.loop_statement, indent+2, "", how_deep-1); break; case stmk_switch: db_statement_list(sp->variant.switch_stmt.body_statement, indent+2, "body ", how_deep-1); break; case stmk_try_block: if (sp->variant.try_block != NULL) { db_statement_list(sp->variant.try_block->statement, indent+2, "", how_deep-1); if (how_deep > 1) { for (hp = sp->variant.try_block->handlers; hp != NULL; hp = hp->next) { db_indent(indent + 2); fprintf(f_debug, "catch%s, at %lu:", hp->parameter == NULL ? " (...)" : "", (unsigned long)hp->catch_position.seq); if (hp->statement->kind == (a_statement_kind)stmk_block) { fputs(" ", f_debug); db_statement(hp->statement); db_statement_list(hp->statement->variant.block.statements, indent+4, "", how_deep-1); } else { fputs("\n", f_debug); db_statement_list(hp->statement, indent+4, "", how_deep-1); } /* if */ } /* for */ #if MICROSOFT_EXTENSIONS_ALLOWED if (sp->variant.try_block->finally_statement != NULL) { db_indent(indent + 2); fprintf(f_debug, "finally:\n"); db_statement_list(sp->variant.try_block->finally_statement, indent+4, "", how_deep-1); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ } /* if */ break; #if MICROSOFT_EXTENSIONS_ALLOWED case stmk_microsoft_try: db_statement_list(sp->variant.microsoft_try->guarded_statement, indent+2, "", how_deep-1); db_indent(indent + 2); if (sp->variant.microsoft_try->except_expr != NULL) { fprintf(f_debug, "__except"); db_expression(sp->variant.microsoft_try->except_expr); } else { fprintf(f_debug, "__finally"); } /* if */ #if EXTRA_SOURCE_POSITIONS_IN_IL if (sp->variant.microsoft_try->except_or_finally_position.seq != 0) { a_microsoft_try_supplement_ptr mtsp = sp->variant.microsoft_try; fprintf(f_debug, ", at %lu/%lu", (unsigned long)mtsp->except_or_finally_position.seq, (unsigned long)mtsp->except_or_finally_position.column); } /* if */ #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ fputc('\n', f_debug); db_statement_list(sp->variant.microsoft_try->cleanup_statement, indent+2, "", how_deep-1); break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ default:; } /* switch */ } /* for */ } /* if */ } /* db_statement_list */ void db_statements(a_statement_ptr statement) /* Dump statements recursively (to a pre-set limit). */ { db_statement_list(statement, 0, "", 10); } /* db_statements */ #endif /* DEBUG */ void set_inline_flag(a_routine_ptr rp, a_boolean flag) /* Set the is_inline flag for the given routine to the given boolean value. Update the dependent flag definition_for_inlining_only if needed: The latter flag should be FALSE if is_inline is FALSE. Also, in Microsoft mode, the combination of dllimport and inline indicates that the definition should only be used for inlining (i.e., the definition_for_inlining_only flag should be set). */ { if (!flag) { rp->definition_for_inlining_only = FALSE; rp->suppress_inline_body = FALSE; } else if (cpp11_mode && strict_ansi_mode && !rp->is_inline && !rp->compiler_generated && rp->defined) { a_symbol_ptr sym = symbol_for(rp); pos2_sy_diagnostic(es_error, ec_first_inline_after_definition, &error_position, &sym->decl_position, sym); flag = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (microsoft_mode && (rp->decl_modifiers & DM_DLLIMPORT)) { rp->definition_for_inlining_only = TRUE; rp->suppress_inline_body = TRUE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if GNU_EXTENSIONS_ALLOWED } else if (rp->never_inline && gnu_mode && (!rp->is_template_function || rp->is_specialized || rp->is_prototype_instantiation) && (!rp->source_corresp.is_class_member || rp->defined_outside_of_parent || find_attribute(ak_always_inline, rp->source_corresp.attributes) != NULL)) { /* Was explicitly marked "noinline" previously and is now explicitly marked inline. (An instance generated from a template is not checked: the declarations of the template itself were.) */ pos_warning(ec_inline_gnu_noinline_conflict, &error_position); #endif /* GNU_EXTENSIONS_ALLOWED */ } /* if */ rp->is_inline = flag; } /* set_inline_flag */ #if !NULL_POINTER_IS_ZERO void clear_decl_pos_block(a_decl_pos_block_ptr decl_pos_block) /* Initialize the fields of the specified decl-pos block. */ { decl_pos_block->decl_pos = null_source_position; decl_pos_block->storage_class_pos = null_source_position; #if EXTRA_SOURCE_POSITIONS_IN_IL decl_pos_block->identifier_range = null_source_range; decl_pos_block->specifiers_range = null_source_range; decl_pos_block->declarator_range = null_source_range; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ decl_pos_block->var_init_range = null_source_range; #if EXTRA_SOURCE_POSITIONS_IN_IL decl_pos_block->extra_positions = NULL; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* clear_decl_pos_block */ #endif /* !NULL_POINTER_IS_ZERO */ void f_add_element_position(an_element_position_kind kind, a_source_position *pos, an_element_position_ptr *p_epp) /* Append to the list pointed to by *p_epp an element position entry of the given kind and for the given position. Upon return *p_epp points to the added element. */ { an_element_position_ptr new_epp = alloc_element_position(); new_epp->kind = kind; new_epp->position = *pos; while (*p_epp != NULL) p_epp = &(*p_epp)->next; *p_epp = new_epp; } /* f_add_element_position */ #if EXTRA_SOURCE_POSITIONS_IN_IL a_decl_position_supplement_ptr make_decl_pos_supplement( a_boolean at_file_scope, a_decl_pos_block_ptr decl_pos_block) /* If the specified decl-pos block pointer is non-NULL, allocate a decl-position-supplement entry, set its values, and return it. at_file_scope is TRUE when the entry should be allocated in the file scope memory region. */ { a_decl_position_supplement_ptr dpsp; if (decl_pos_block != NULL) { dpsp = alloc_decl_position_supplement(at_file_scope); dpsp->identifier_range = decl_pos_block->identifier_range; dpsp->specifiers_range = decl_pos_block->specifiers_range; dpsp->variant.declarator_range = decl_pos_block->declarator_range; dpsp->extra_positions = decl_pos_block->extra_positions; } else { dpsp = NULL; } /* if */ return dpsp; } /* make_decl_pos_supplement */ void update_decl_pos_info(a_source_correspondence *scp, a_decl_pos_block_ptr decl_pos_block) /* If the specified decl-pos block pointer is non-NULL, set the values in the decl-position-supplement entry pointed to from the specified source correspondence entry. */ { a_decl_position_supplement_ptr dpsp; if (decl_pos_block != NULL) { dpsp = scp->decl_pos_info; if (dpsp != NULL) { dpsp->identifier_range = decl_pos_block->identifier_range; dpsp->specifiers_range = decl_pos_block->specifiers_range; dpsp->variant.declarator_range = decl_pos_block->declarator_range; dpsp->extra_positions = decl_pos_block->extra_positions; } /* if */ } /* if */ } /* update_decl_pos_info */ void prepend_element_positions(an_element_position_ptr new_epp, an_element_position_ptr *p_epp) /* If new_epp is non-NULL, insert the list of element positions it points to in front of the list pointed by *p_epp (which might be NULL). */ { if (new_epp != NULL) { /* The newly inserted list is not empty. */ an_element_position_ptr old_epp = *p_epp; *p_epp = new_epp; if (old_epp != NULL) { /* The previous list was not empty: Append it at the end. */ while (*p_epp != NULL) p_epp = &(*p_epp)->next; *p_epp = old_epp; } /* if */ } /* if */ } /* prepend_element_positions */ #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ #endif /* !STANDALONE_UTILITY_PROGRAM */ int compare_source_positions(const a_source_position *pos1, const a_source_position *pos2) /* Compare two source positions. Return +1 if pos1 is greater than pos2. Return 0 if pos1 is equal to pos2. Return -1 if pos1 is less than pos2. */ { int result; a_seq_number seq1 = pos1->seq; a_seq_number seq2 = pos2->seq; if (seq1 != seq2) { result = (seq1 > seq2) ? 1 : -1; } else { /* If the sequence numbers are equal, check the column numbers. */ a_column_number column1 = pos1->column; a_column_number column2 = pos2->column; result = (column1 == column2) ? 0 : ((column1 > column2) ? 1 : -1); } /* if */ return result; } /* compare_source_positions */ #define TEMP_TEXT_BUFFER_INCREMENTAL_ALLOCATION 2000 /* Initial and incremental allocation size for temp_text_buffer. The initial allocation should be such that almost all cases can be accepted (so that the realloc is hardly ever needed). */ void expand_temp_text_buffer(sizeof_t size_needed) /* Expand the temp_text_buffer by reallocating it, so that its total size is at least size_needed. Called by ensure_temp_text_buffer_space. */ { sizeof_t new_size; new_size = size_temp_text_buffer + TEMP_TEXT_BUFFER_INCREMENTAL_ALLOCATION; if (new_size < size_needed) new_size = size_needed; if (temp_text_buffer_managed) { char *old_buffer = temp_text_buffer; temp_text_buffer = alloc_fe(new_size); memcpy(temp_text_buffer, old_buffer, size_temp_text_buffer); free_fe(old_buffer, size_temp_text_buffer); } else { temp_text_buffer = realloc_buffer(temp_text_buffer, size_temp_text_buffer, new_size); } /* if */ size_temp_text_buffer = new_size; } /* expand_temp_text_buffer */ void put_str_to_temp_text_buffer(a_const_char *str) /* Output the indicated string to temp_text_buffer at the position indicated by pos_in_temp_text_buffer, and update the latter. The terminating null character is copied but not counted in updating pos_in_temp_text_buffer. */ { sizeof_t len = strlen(str); sizeof_t new_size = pos_in_temp_text_buffer + len; ensure_temp_text_buffer_space(new_size+1); (void)strcpy(temp_text_buffer+pos_in_temp_text_buffer, str); pos_in_temp_text_buffer = new_size; } /* put_str_to_temp_text_buffer */ void put_uint_to_temp_text_buffer(unsigned long long value) /* Output the indicated unsigned value as a string to temp_text_buffer at the position indicated by pos_in_temp_text_buffer, and update the latter. */ { char buffer[50]; (void)snprintf(buffer, sizeof(buffer), "%llu", value); put_str_to_temp_text_buffer(buffer); } /* put_uint_to_temp_text_buffer */ void put_str_to_temp_text_buffer_octl( a_const_char *str, ARG_UNUSED an_il_to_str_output_control_block_ptr octl) /* Wrapper for put_str_to_temp_text_buffer for use with the il-to-str routines. */ { put_str_to_temp_text_buffer(str); } /* put_str_to_temp_text_buffer_octl */ void put_ch_to_temp_text_buffer(char ch) /* Output the indicated character to temp_text_buffer at the position indicated by pos_in_temp_text_buffer, and update the latter. */ { sizeof_t new_size = pos_in_temp_text_buffer + 1; ensure_temp_text_buffer_space(new_size); temp_text_buffer[pos_in_temp_text_buffer++] = ch; } /* put_ch_to_temp_text_buffer */ static void reset_seq_cache(void) /* Clear the cached information used to convert sequence number to source file and line number. */ { /* Reset the entry that stores the status of the last sequence number to source file/line conversion. */ seq_cache.first_seq_number = 0; seq_cache.last_seq_number = 0; seq_cache.line_offset = 0; seq_cache.physical_line = FALSE; seq_cache.source_file = NULL; } /* reset_seq_cache */ static void expand_seq_number_lookup_table(unsigned long new_entries) /* Increase the size of the sequence number lookup table. new_entries specifies the size of the new table. If it is zero, the routine determines the size to be used. */ { sizeof_t old_size = seq_number_lookup_table_size * sizeof(a_seq_number_lookup_entry_ptr); sizeof_t new_size; if (new_entries != 0) { /* Use the value provided by the caller. */ } else if (seq_number_lookup_table_size == 0) { /* If this is the first allocation, use the special initial allocation size. */ new_entries = SEQ_NUMBER_LOOKUP_TABLE_INITIAL_ALLOCATION; } else { /* Otherwise, double the size of the table. */ new_entries = seq_number_lookup_table_size * 2; } /* if */ new_size = new_entries * sizeof(a_seq_number_lookup_entry_ptr); seq_number_lookup_table = (a_seq_number_lookup_entry_ptr*) realloc_buffer((char*)seq_number_lookup_table, old_size, new_size); seq_number_lookup_table_size = new_entries; } /* expand_seq_number_lookup_table */ static void build_seq_number_lookup_table(unsigned long num_entries) /* This routine is used to reconstruct the sequence number lookup table. It is called after the IL has been read from a file, or after memory has been restored from a precompiled header file. num_entries is the number of entries in the table, if known. If it is not known, a value of zero should be used, in which case the routine will build the table, expanding it as necessary. */ { a_seq_number_lookup_entry_ptr snlep; unsigned long entry_number; /* If the size of the table is known, make sure the table is large enough. */ if (num_entries != 0 && num_entries > seq_number_lookup_table_size) { expand_seq_number_lookup_table(num_entries); } /* if */ for (snlep = il_header.seq_number_lookup_entries, entry_number = 0; snlep != NULL; snlep = snlep->next) { if (entry_number >= seq_number_lookup_table_size) { /* The table is not big enough. Increase the size. */ expand_seq_number_lookup_table((unsigned long)0); } /* if */ seq_number_lookup_table[entry_number++] = snlep; } /* for */ /* Record the table size in the IL header. */ il_header.num_seq_number_lookup_entries = entry_number; okay_to_use_seq_number_lookup_table = TRUE; } /* build_seq_number_lookup_table */ #if DEBUG void db_seq_number_lookup_table(void) /* Display the sequence number lookup table, for debugging purposes. */ { unsigned long count = 0; a_seq_number_lookup_entry_ptr snlep; for (snlep = il_header.seq_number_lookup_entries; snlep != NULL; snlep = snlep->next) { count++; fprintf(f_debug, " first=%8lu, last=%8lu, line=%8lu, file=%s\n", (unsigned long)snlep->first, (unsigned long)snlep->last, (unsigned long)snlep->line_number, snlep->source_file->file_name); } /* for */ if (count != il_header.num_seq_number_lookup_entries) { fprintf(f_debug, " *** Sequence number count mismatch ***\n"); fprintf(f_debug, " il_header=%lu, list=%lu\n", (unsigned long)il_header.num_seq_number_lookup_entries, (unsigned long)count); } /* if */ } /* db_seq_number_lookup_table */ void db_source_file_for_seq_info(void) /* Display some information about the sequence number to source file translation process. Specifically, this does the conversion for the special end-of-source sequence number, and for the last actual sequence number of the translation unit. */ { a_line_number line; a_boolean at_end_of_source; a_source_file_ptr sfp; db_seq_number_lookup_table(); sfp = source_file_for_seq(curr_seq_number, &line, &at_end_of_source, /*physical_line=*/FALSE); fprintf(f_debug, "End of source, physical_line=FALSE\n"); fprintf(f_debug, "file=%s, line=%lu, end-of-source=%d\n", sfp ? sfp->file_name : "", (unsigned long)line, at_end_of_source); sfp = source_file_for_seq(curr_seq_number, &line, &at_end_of_source, /*physical_line=*/TRUE); fprintf(f_debug, "End of source, physical_line=TRUE\n"); fprintf(f_debug, "file=%s, line=%lu, end-of-source=%d\n", sfp ? sfp->file_name : "", (unsigned long)line, at_end_of_source); sfp = source_file_for_seq(curr_seq_number - 1, &line, &at_end_of_source, /*physical_line=*/FALSE); fprintf(f_debug, "Last line of file, physical_line=FALSE\n"); fprintf(f_debug, "file=%s, line=%lu, end-of-source=%d\n", sfp ? sfp->file_name : "", (unsigned long)line, at_end_of_source); sfp = source_file_for_seq(curr_seq_number - 1, &line, &at_end_of_source, /*physical_line=*/TRUE); fprintf(f_debug, "Last line of file, physical_line=TRUE\n"); fprintf(f_debug, "file=%s, line=%lu, end-of-source=%d\n", sfp ? sfp->file_name : "", (unsigned long)line, at_end_of_source); } /* db_source_file_for_seq_info */ #endif /* DEBUG */ #if !STANDALONE_UTILITY_PROGRAM void switch_il_region(a_memory_region_number region_number) /* Change the current IL memory region to "region_number". */ { curr_il_region_number = region_number; #if DEBUG if (debug_level >= 5) { fprintf(f_debug, "Switching to IL region %d.\n", curr_il_region_number); } /* if */ #endif /* DEBUG */ } /* switch_il_region */ void switch_to_file_scope_region( a_memory_region_number *region_to_switch_back_to) /* Switch to the file-scope memory region if not already there. Set region_to_switch_back_to for use later by switch_back_to_original_region. */ { if (curr_il_region_number != file_scope_region_number) { *region_to_switch_back_to = curr_il_region_number; switch_il_region(file_scope_region_number); } else { *region_to_switch_back_to = NULL_region_number; } /* if */ } /* switch_to_file_scope_region */ void switch_to_scope_region(a_scope_depth scope_depth, a_memory_region_number *region_to_switch_back_to) /* Switch to the memory region associated with the given scope depth if not already there. Set region_to_switch_back_to for use later by switch_back_to_original_region. */ { a_memory_region_number region; region = scope_stack[scope_depth].il_memory_region; if (curr_il_region_number != region) { *region_to_switch_back_to = curr_il_region_number; switch_il_region(region); } else { *region_to_switch_back_to = NULL_region_number; } /* if */ } /* switch_to_scope_region */ void switch_back_to_original_region( a_memory_region_number region_to_switch_back_to) /* Switch back to the memory region that was current when switch_to_file_scope_region was called. */ { if (region_to_switch_back_to != NULL_region_number) { switch_il_region(region_to_switch_back_to); } /* if */ } /* switch_back_to_original_region */ a_scope_ptr new_file_scope(a_scope_number scope_number) /* Start a new file scope IL memory region for file scope specified by scope_number. Establish this new region as the current IL region. Allocate a scope entry in the new region and return a pointer to it. */ { a_scope_ptr sp; /* The file scope memory region is created in initialization. */ switch_il_region(file_scope_region_number); /* Allocate the IL scope entry. */ sp = alloc_scope((a_scope_kind)sck_file, scope_number, (a_routine_ptr)NULL); /* Remember the location of the primary scope entry. */ il_header.region_scope_entry[curr_il_region_number] = sp; return sp; } /* new_file_scope */ a_scope_ptr new_function_scope(a_scope_number scope_number, a_routine_ptr assoc_routine, a_memory_region_number memory_region) /* Allocate a new function scope for the function specified by assoc_routine and scope_number. memory_region is the memory region number to be used for this routine, or NULL_region_number if a new memory region is to be created. The current IL region is set to either memory_region or the newly created memory region. The new scope is created in the appropriate memory region and a pointer to it is returned. */ { a_function_def_descr_ptr fddp; a_function_def_number function_def_number; a_scope_ptr sp, top_scope; check_assertion(assoc_routine != NULL); function_def_number = new_function_def_number(); fddp = &il_header.function_def_table[function_def_number]; if (memory_region == NULL_region_number) { /* No previous memory region is being used. Create a new one. Mark the scope as the primary entry for the memory region. */ fddp->memory_region = new_memory_region(); assoc_routine->is_top_level_in_mem_region = TRUE; } else { /* Use the specified memory region. */ check_assertion(assoc_routine->function_def_number != NO_FUNCTION_DEF_NUMBER); fddp->memory_region = memory_region; } /* if */ /* Switch to the function's memory region. */ switch_il_region(fddp->memory_region); /* Allocate the IL scope entry. */ sp = alloc_scope((a_scope_kind)sck_function, scope_number, assoc_routine); /* Remember the location of the primary scope entry. */ fddp->scope = sp; assoc_routine->function_def_number = function_def_number; assoc_routine->memory_region = fddp->memory_region; /* Link this on the list of scopes in the region. */ top_scope = il_header.region_scope_entry[curr_il_region_number]; if (top_scope == NULL) { /* The usual case: A top-level function scope. */ il_header.region_scope_entry[curr_il_region_number] = sp; } else { /* A non-top-level function scope (i.e., a member function of a local class, such as a lambda closure). Insert it after the top-level function scope. */ a_scope_ptr tail_scopes = top_scope->next; if (tail_scopes != NULL) { tail_scopes->prev = sp; sp->next = tail_scopes; } /* if */ top_scope->next = sp; sp->prev = top_scope; } /* if */ #if DEBUG if (db_flag_is_set("new_function_scope")) { db_scope(sp); fprintf(f_debug, ", region=%ld, function_def=%ld\n", (long)assoc_routine->memory_region, (long)assoc_routine->function_def_number); } /* if */ #endif /* DEBUG */ return sp; } /* new_function_scope */ /* Forward declaration. */ static void update_seq_cache(a_source_file_ptr source_file, a_seq_number first_seq_number, a_seq_number last_seq_number, long line_offset, a_boolean physical_line); static void add_seq_number_lookup_entry(a_source_file_ptr source_file, a_seq_number seq_number, a_line_number line_number) /* Add an entry to the list of sequence number lookup entries. The source file begins at the sequence number and line number specified by seq_number and line_number, respectively. The ending sequence number is not known yet. The sequence number lookup table is a dynamically allocated array of pointers to sequence number lookup table entries. The entries in the array are in sequence number order so that a binary search can be used to find a given sequence number. The table contains entries that reflect any #line directives in the program, so this lookup mechanism is only suitable for sequence number lookups that are not seeking the actual physical line of the input file. A linked list of lookup entries is maintained so that they can be easily be written as part of the IL file. Because the lookup table is reallocated as needed, it is stored in general memory, not IL memory. The table is reconstructed from the individual entries when the IL is read from a file, or when a precompiled header file is used. */ { a_seq_number_lookup_entry_ptr snlep; #if DEBUG if (db_flag_is_set("seq_number_lookup")) { fprintf(f_debug, "Created seq lookup %lu for file %s seq %lu line %lu\n", il_header.num_seq_number_lookup_entries, source_file->file_name, (unsigned long)seq_number,(unsigned long)line_number); } /* if */ #endif /* DEBUG */ if (curr_seq_number_lookup_entry != NULL && curr_seq_number_lookup_entry->first == seq_number) { #if EXPENSIVE_CHECKING && DEBUG /* Verify that the sequence number lookup entry we're trying to reuse either comes from a file that was never finished, or has been explicitly marked as recyclable. */ check_assertion(curr_seq_number_lookup_entry->last == MAX_SEQ_NUMBER || curr_seq_number_lookup_entry->is_marked_for_recycle); #endif /* EXPENSIVE_CHECKING && DEBUG */ /* The previously created entry turns out not to be needed. Reuse that entry instead of adding a new one. */ snlep = curr_seq_number_lookup_entry; } else { if (curr_seq_number_lookup_entry != NULL && curr_seq_number_lookup_entry->last == MAX_SEQ_NUMBER) { /* Set the ending sequence number of the previous entry. Don't set the value if it has already been set (this code would give the wrong value in such cases). */ curr_seq_number_lookup_entry->last = seq_number - 1; } /* if */ #if EXPENSIVE_CHECKING if (curr_seq_number_lookup_entry != NULL) { /* Make sure the sequence numbers are in increasing order. */ check_assertion(curr_seq_number_lookup_entry->first < seq_number); } /* if */ #endif /* EXPENSIVE_CHECKING */ snlep = alloc_seq_number_lookup_entry(); if (il_header.num_seq_number_lookup_entries >= seq_number_lookup_table_size) { /* There is not enough space in the table for the new entry. Expand it. */ expand_seq_number_lookup_table((unsigned long)0); } /* if */ /* Set the IL header field to point to the head of the list of lookup entries. */ if (il_header.seq_number_lookup_entries == NULL) { il_header.seq_number_lookup_entries = snlep; } else { /* Link this entry into the list of lookup entries. */ check_assertion(curr_seq_number_lookup_entry != NULL); curr_seq_number_lookup_entry->next = snlep; } /* if */ seq_number_lookup_table[il_header.num_seq_number_lookup_entries] = snlep; il_header.num_seq_number_lookup_entries++; /* Save the pointer to this entry so that it can be updated later. */ curr_seq_number_lookup_entry = snlep; } /* if */ snlep->source_file = source_file; snlep->first = seq_number; snlep->last = MAX_SEQ_NUMBER; snlep->line_number = line_number; /* It is likely that the next sequence number translation will refer to this entry, so set the cache to point to it. */ update_seq_cache(source_file, seq_number, MAX_SEQ_NUMBER, -(long)seq_number + line_number, FALSE); } /* add_seq_number_lookup_entry */ void record_start_of_source_file(a_source_file_ptr parent_file, a_seq_number seq_number, a_line_number line_number, a_const_char *file_name, a_const_char *full_name, a_const_char *name_as_written, a_source_file_ptr *new_file, a_boolean is_include_file, a_boolean is_system_include, a_boolean is_preinclude, a_boolean preinclude_macros_only, ARG_UNUSED a_boolean is_implicit_include, a_boolean from_system_include_dir, ARG_UNUSED a_boolean is_assembly_file) /* Create a source file entry in the intermediate language, to record the start of a new source file (either the primary source file, an include file, or a C++/CLI assembly). Line line_number of the new file is sequence number seq_number of the compilation, and the new file's short and long form names are file_name and full_name. The parent file for this file is parent_file; if there is no parent file (i.e., for the primary source file), parent_file == NULL. For entries generated by #line directives, full_name == NULL and name_as_written == NULL. When the name is manufactured as part of automatic instantiation, name_as_written is also NULL. is_include_file is TRUE for files that are explicitly or implicitly included. is_system_include is TRUE for files included with the #include notation and FALSE for all other files. is_preinclude is TRUE for files included via the --preinclude (or for assemblies, the --preusing) command-line option. from_system_include_dir is TRUE if the file was found in a directory marked as a system include directory. is_assembly_file is TRUE if the source file is a C++/CLI assembly file (i.e., a file that contains metadata). */ { a_source_file_ptr sfp; db_enter(5, "record_start_of_source_file"); #if DEBUG if (debug_level >= 5) { fprintf(f_debug, "file = \"%s\", seq = %lu\n", file_name, (unsigned long)seq_number); } /* if */ #if EXPENSIVE_CHECKING { a_seq_number_lookup_entry_ptr snlep = il_header.seq_number_lookup_entries; /* Find the latest sequence number lookup entry, if any. */ while (snlep != NULL && snlep->next != NULL) { snlep = snlep->next; } /* while */ if (is_implicit_include) { /* Validate that implicit includes are only performed on fully sequenced translation units, then mark the translation unit as reopened. */ check_assertion(curr_translation_unit->is_fully_sequenced); curr_translation_unit->is_fully_sequenced = FALSE; } /* if */ /* By this point the current translation unit should either have already been an open translation unit, or it should have been reopened by an implicit include. All other cases are contract violations. */ check_assertion(!curr_translation_unit->is_fully_sequenced); /* If the sequence number is one this is a brand new sequence either from a fresh compilation or a lexical reset. If the last sequence number encountered is MAX_SEQ_NUMBER it will be updated by this function. In either case, we cannot perform any meaningful checks about the sequencing. */ if (seq_number != 1 && snlep->last != MAX_SEQ_NUMBER) { a_boolean is_new_translation_unit = !curr_translation_unit->is_partially_sequenced; uint32_t expected_sequence_offset = is_new_translation_unit ? 2 : 1; /* There is a previous lookup entry that we can meaningfully examine. Check to make sure it ends before this file starts (factoring in the sequence skip between new translation units). */ check_assertion(snlep->last == seq_number - expected_sequence_offset); } /* if */ /* Mark the translation unit as partially sequenced so reentering this function ensures no unintended sequence skips. */ curr_translation_unit->is_partially_sequenced = TRUE; } #endif /* EXPENSIVE_CHECKING */ #endif /* DEBUG */ /* Allocate the new file block. */ *new_file = sfp = alloc_source_file(); sfp->file_name = file_name; sfp->full_name = full_name; sfp->name_as_written = name_as_written; sfp->first_seq_number = seq_number; sfp->first_line_number= line_number; sfp->is_include_file = is_include_file; sfp->included_by_system_include = is_system_include; sfp->included_by_preinclude = is_preinclude; sfp->preinclude_macros_only = preinclude_macros_only; sfp->from_system_include_dir = from_system_include_dir; #if INSTANTIATION_BY_IMPLICIT_INCLUSION sfp->is_implicit_include = is_implicit_include; #endif /* INSTANTIATION_BY_IMPLICIT_INCLUSION */ #if MICROSOFT_EXTENSIONS_ALLOWED sfp->is_assembly_file = is_assembly_file; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Link the parent or the preceding sibling file to this one. */ if (parent_file == NULL) { /* No parent, so link the il_header to this primary source file. When exported templates are processed, secondary files can be read, and they are added to the end of the top-level list. */ a_source_file_ptr last_primary_file = il_header.primary_source_file; sfp->top_level_file = TRUE; if (last_primary_file == NULL) { il_header.primary_source_file = sfp; } else { while (last_primary_file->next != NULL) { last_primary_file = last_primary_file->next; } /* while */ last_primary_file->next = sfp; } /* if */ } else { if (parent_file->first_child_file == NULL) { /* First child for this parent. */ parent_file->first_child_file = sfp; } else { /* Not first child, link previous child to this one. */ parent_file->last_child_file->next = sfp; } /* if */ parent_file->last_child_file = sfp; #if INSTANTIATION_BY_IMPLICIT_INCLUSION if (parent_file == il_header.primary_source_file) { /* Update the parent file's last sequence number. This is required when an implicitly included template definition file is started after the end of the primary source file has already been recorded. */ parent_file->last_seq_number = MAX_SEQ_NUMBER; } /* if */ #endif /* INSTANTIATION_BY_IMPLICIT_INCLUSION */ } /* if */ /* Add an entry to the sequence number lookup table to reflect this source file entry. */ add_seq_number_lookup_entry(sfp, sfp->first_seq_number, sfp->first_line_number); db_exit(); } /* record_start_of_source_file */ void record_inclusion_of_module_source_file( a_const_char *file_name, a_source_position *starting_seq_num, a_module_ptr mod, uint32_t max_line_number) /* Record sequence number information for a source file referenced in a module file. Module files (such as IFC files) may record file/line/column information for entities, but information about the sequence of source file includes may not be maintained. Additionally, the specified file may not even be accessible on this system (it may be an include file on a system on which the module was compiled). The module including this source file is given by mod. This function creates a record of the source file, with max_line_number lines that are associated with the source file, then resumes the previous file. The initial source position for the file is returned in *starting_seq_num. */ { a_boolean is_implicit_include = curr_ise == NULL; a_source_file_ptr parent_file, new_file; check_assertion(curr_seq_number_lookup_entry != NULL && max_line_number >= 1); parent_file = curr_seq_number_lookup_entry->source_file; check_assertion(parent_file != NULL); if (is_implicit_include) { /* Decrement the sequence number when starting an implicitly included file so that we reuse the old end-of-source position. */ seq_number_last_read--; } /* if */ /* Record the new source file. Specify an unknown line number to suppress line number information in any diagnostics. */ record_start_of_source_file(parent_file, seq_number_last_read + 1, (a_line_number)SP_LINE_UNKNOWN, file_name, file_name, file_name, &new_file, /*is_include_file=*/TRUE, /* presumably */ /*is_system_include=*/FALSE, /* presumably */ /*is_preinclude=*/FALSE, /*preinclude_macros_only=*/FALSE, is_implicit_include, /*from_system_include_dir=*/FALSE, /*is_assembly_file=*/FALSE); new_file->assoc_module = mod; set_position_to(*starting_seq_num, seq_number_last_read + 1, SP_COL_UNKNOWN); if (!checked_addition(&seq_number_last_read, seq_number_last_read + 1, max_line_number)) { /* Check for overflow in the sequence number calculation. Such overflow will break the source inclusion system, resulting in sequence numbers that can't be resolved to a file. */ unexpected_condition(); } /* if */ record_end_of_source_file(new_file, seq_number_last_read); if (is_implicit_include) { /* Emulate the end of source logic that would normally be handled by read_logical_source_line when finishing an implicit include -- as we don't actually read any source lines here. Note that unlike read_logical_source_line, the curr_seq_number is not updated, as we're not actually modifying the token stream, just expanding the translation unit's line count to include an end-of-source position. */ ++seq_number_last_read; #if EXPENSIVE_CHECKING && DEBUG curr_translation_unit->is_fully_sequenced = TRUE; #endif /* EXPENSIVE_CHECKING && DEBUG */ } else { /* Resume the previous file where we left off. */ record_resumption_of_source_file(parent_file, seq_number_last_read + 1, curr_ise->line_number + 1); } /* if */ } /* record_inclusion_of_module_source_file */ #if MICROSOFT_EXTENSIONS_ALLOWED void record_inclusion_of_assembly_source_file( a_const_char *file_name, a_const_char *full_name, a_const_char *name_as_written, a_source_file_ptr *new_file, a_boolean is_system_include, a_boolean is_preinclude, a_source_position *inserted_position) /* Record sequence number information for a C++/CLI assembly. Assemblies aren't read like normal source files, so they don't have typical sequence number entries associated with them. In order to map errors in an assembly to a particular assembly file, this function creates a record of the assembly as a source file, then creates a single (bogus) sequence number that is associated with the assembly source file, then resumes the previous file. The result is a single position (returned in *inserted_position), which can be used as position information for any metadata scanned from the assembly. file_name is the display name of the assembly; full_name is the actual name. name_as_written is the name of the assembly as it appears in the #using directive or on the command line. *new_file is set to the a_source_file entry for this assembly. is_system_include is TRUE when the assembly is included with #using rather than #using "file.dll". is_preinclude is TRUE when using --preusing to include the assembly. */ { a_source_file_ptr parent_file; check_assertion(curr_seq_number_lookup_entry != NULL); parent_file = curr_seq_number_lookup_entry->source_file; check_assertion(parent_file != NULL); /* Record the assembly as a new source file. Specify an unknown line number to suppress line number information in any diagnostics. */ record_start_of_source_file(parent_file, seq_number_last_read + 1, (a_line_number)SP_LINE_UNKNOWN, file_name, full_name, name_as_written, new_file, /*is_include_file=*/TRUE, is_system_include, is_preinclude, /*preinclude_macros_only=*/FALSE, /*is_implicit_include=*/FALSE, /*from_system_include_dir=*/FALSE, /*is_assembly_file=*/TRUE); /* Increment the sequence number to create a unique number that can be mapped to this assembly. */ curr_seq_number = ++seq_number_last_read; set_position_to(*inserted_position, curr_seq_number, SP_COL_UNKNOWN); record_end_of_source_file(*new_file, seq_number_last_read); /* Resume the previous file where we left off. */ record_resumption_of_source_file(parent_file, seq_number_last_read + 1, curr_ise->line_number + 1); } /* record_inclusion_of_assembly_source_file */ a_cli_metadata_file_ptr map_assembly_index_to_cmfp( an_assembly_index assembly_index) /* Utility to return a pointer to the a_cli_metadata_file structure associated with the specified assembly index. Returns NULL if not found. */ { a_cli_metadata_file_ptr cmfp; for (cmfp = il_header.cli_metadata_files; cmfp != NULL; cmfp = cmfp->next) { if (cmfp->assembly_index == assembly_index) break; } /* for */ return cmfp; } /* map_assembly_index_to_cmfp */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ void record_resumption_of_source_file(a_source_file_ptr curr_file, a_seq_number seq_number, a_line_number line_number) /* Indicate that we are resuming the processing of curr_file at the sequence number specified by seq_number and line number specified by line_number. If curr_file is NULL, we are processing a #line directive that changes the line number in the file designated by curr_ise->assoc_il_file, which requires that we end that file and start a new one to reflect the jump in line numbers before recording the resumption. */ { if (curr_file == NULL) { /* We are resuming the current source file at a different line number, so we need to end the current source file and create a new one to represent the new range of line numbers. */ a_source_file_ptr old_file = curr_ise->assoc_il_file; curr_file = clone_current_input_file(seq_number, line_number); record_end_of_source_file(old_file, seq_number_last_read); } /* if */ add_seq_number_lookup_entry(curr_file, seq_number, line_number); } /* record_resumption_of_source_file */ void record_end_of_source_file(a_source_file_ptr curr_file, a_seq_number seq_number) /* Finish the source file entry for the source file indicated by curr_file, by recording that the last sequence number contained therein is seq_number. */ { db_enter(5, "record_end_of_source_file"); #if DEBUG if (debug_level >= 5) { fprintf(f_debug, "seq = %lu\n", (unsigned long)seq_number); } /* if */ #endif /* DEBUG */ curr_file->last_seq_number = seq_number; /* Clear the cached sequence number conversion information. */ reset_seq_cache(); /* Update the current sequence number lookup entry with the ending sequence number. */ curr_seq_number_lookup_entry->last = seq_number; #if EXPENSIVE_CHECKING && DEBUG if (curr_seq_number_lookup_entry->first > seq_number) { curr_seq_number_lookup_entry->is_marked_for_recycle = TRUE; } /* if */ #endif /* EXPENSIVE_CHECKING && DEBUG */ db_exit(); } /* record_end_of_source_file */ #endif /* !STANDALONE_UTILITY_PROGRAM */ #if DEBUG void db_indent(size_t indent) /* Indent the current line by "indent" characters. */ { fprintf(f_debug, "%*s", (int)indent, ""); } /* db_indent */ static void db_source_file_seq_info(a_source_file_ptr sfp, size_t indent) /* Display the sequence number information associated with a source file. */ { for (; sfp != NULL; sfp = sfp->next) { db_indent(indent); fprintf(f_debug, "Source file seq. info for: %s\n", sfp->file_name); db_indent(indent); fprintf(f_debug, "First_seq: %lu, last_seq: %lu\n", (unsigned long)sfp->first_seq_number, (unsigned long)sfp->last_seq_number); db_indent(indent); fprintf(f_debug, "First_line_number: %lu\n", (unsigned long)sfp->first_line_number); if (sfp->first_child_file != NULL) { db_source_file_seq_info(sfp->first_child_file, indent+2); } /* if */ } /* for */ } /* db_source_file_seq_info */ #endif /* DEBUG */ static a_source_file_ptr find_file_for_seq(a_seq_number seq_number) /* Convert a sequence number into the containing primary source file. */ { a_source_file_ptr curr_file; /* Find the top-level file for this sequence number. */ curr_file = il_header.primary_source_file; check_assertion(curr_file != NULL); check_assertion(seq_number >= curr_file->first_seq_number); while (seq_number-1 > curr_file->last_seq_number) { curr_file = curr_file->next; #if CHECKING if (curr_file == NULL) { #if DEBUG if (debug_level > 0) { fprintf(f_debug, "seq number = %lu\n", (unsigned long)seq_number); } /* if */ #endif /* DEBUG */ internal_error("find_file_for_seq: bad seq number"); } /* if */ #endif /* CHECKING */ } /* while */ return curr_file; } /* find_file_for_seq */ a_source_file_ptr primary_source_file_for_seq(a_seq_number seq_number) /* Find the primary source file within which the sequence number seq_number falls and return a pointer to it. */ { a_source_file_ptr curr_file; if (seq_number == 0 || il_header.primary_source_file == NULL) { /* Unknown position or no files. */ curr_file = NULL; } else { curr_file = find_file_for_seq(seq_number); } /* if */ return curr_file; } /* primary_source_file_for_seq */ static void update_seq_cache(a_source_file_ptr source_file, a_seq_number first_seq_number, a_seq_number last_seq_number, long line_offset, a_boolean physical_line) /* Update the cache entry used to speed up sequence number to file and line number conversions. */ { seq_cache.first_seq_number = first_seq_number; seq_cache.last_seq_number = last_seq_number; seq_cache.line_offset = line_offset; seq_cache.physical_line = physical_line; seq_cache.source_file = source_file; #if DEBUG if (debug_level >= 5) { fprintf(f_debug, "Cached source sequence conversion information:\n"); fprintf(f_debug, " file=%s\n", source_file->file_name); fprintf(f_debug, " first_seq_number: %lu\n", (unsigned long)seq_cache.first_seq_number); fprintf(f_debug, " last_seq_number: %lu\n", (unsigned long)seq_cache.last_seq_number); fprintf(f_debug, " line_offset: %ld\n", seq_cache.line_offset); fprintf(f_debug, " physical_line: %d\n", seq_cache.physical_line); } /* if */ #endif /* DEBUG */ } /* update_seq_cache */ static a_source_file_ptr find_seq_in_lookup_table( a_seq_number seq_number, a_line_number *line_number, a_boolean *at_end_of_source) /* Convert a sequence number into the source file containing the specified line, and the line number offset within that file. See source_file_for_seq for more information. This routine is used for searches that respect #line directives (i.e., that are not looking for a physical line). This lookup uses the sequence number lookup table. */ { a_seq_number_lookup_entry_ptr snlep_found = NULL; a_source_file_ptr curr_file; long line_offset; ptrdiff_t idx; /* Check whether this is the end-of-file position for any of the translation units being processed. */ for (curr_file = il_header.primary_source_file; curr_file != NULL; curr_file = curr_file->next) { if (seq_number-1 == curr_file->last_seq_number) { /* At end of source. Use the last line of the primary source file. */ *at_end_of_source = TRUE; seq_number--; break; } /* if */ } /* for */ /* The lookup table is ordered by increasing last sequence number (with a possible duplicate last value when a source file ends in an include). Find the first entry whose last sequence number is not less than the one we are looking for. */ auto get_last = [](size_t table_idx) { return seq_number_lookup_table[table_idx]->last; }; idx = low_bound((size_t)il_header.num_seq_number_lookup_entries, seq_number, get_last); if (idx != -1) { if (*at_end_of_source) { /* When a source file ends with an include, the included file and the source file have the same ending sequence number. Skip include entries so that we return the primary source file. Note also that when the primary source file ends in an include, the lookup entry will actually have a starting sequence number one greater than the ending sequence number. */ for (; (unsigned long)idx < il_header.num_seq_number_lookup_entries; ++idx) { a_seq_number_lookup_entry_ptr snlep = seq_number_lookup_table[idx]; if (snlep->last != seq_number) { break; } /* if */ if (!snlep->source_file->is_include_file) { snlep_found = snlep; break; } /* if */ } /* for */ } else { a_seq_number_lookup_entry_ptr snlep = seq_number_lookup_table[idx]; if (seq_number >= snlep->first && seq_number <= snlep->last) { snlep_found = snlep; } /* if */ } /* if */ } /* if */ check_assertion_str2(snlep_found != NULL, "find_seq_in_lookup_table:", "seq_number not found"); curr_file = snlep_found->source_file; line_offset = -(long)(snlep_found->first) + snlep_found->line_number; /* Save information about this conversion so that subsequent conversions can be done more quickly. */ update_seq_cache(curr_file, snlep_found->first, snlep_found->last, line_offset, /*physical_line=*/FALSE); /* Compute the line number to be returned to the caller. */ *line_number = seq_number + (a_line_number)line_offset; return curr_file; } /* find_seq_in_lookup_table */ static a_boolean is_at_end_of_translation_unit_file( a_source_file_ptr trans_unit_file, a_seq_number seq_number) /* Return TRUE if the given sequence number represents the end of the given translation unit file. */ { return seq_number-1 == trans_unit_file->last_seq_number; } /* is_at_end_of_translation_unit_file */ a_boolean is_at_end_of_translation_unit(a_seq_number seq_number) /* Return TRUE if the given sequence number represents the end of its respective translation unit file. */ { a_source_file_ptr trans_unit_file = primary_source_file_for_seq(seq_number); return trans_unit_file != NULL && is_at_end_of_translation_unit_file(trans_unit_file, seq_number); } /* is_at_end_of_translation_unit */ static a_source_file_ptr find_seq_in_source_files( a_seq_number seq_number, a_line_number *line_number, a_boolean *at_end_of_source, a_boolean physical_line) /* Convert a sequence number into the source file containing the specified line, and the line number offset within that file. See source_file_for_seq for more information. This routine is typically used for searches that ignore #line directives (i.e., that are looking for a physical line), but the physical_line parameter is provided should there be need to respect line directives. This routine uses the source file structure to do the conversion. */ { a_source_file_ptr curr_file = find_file_for_seq(seq_number); a_source_file_ptr child_file; a_source_file_ptr grandchild_file; a_source_file_ptr phys_curr_file = NULL; a_source_file_ptr orig_curr_file; a_seq_number first_seq_for_cache; a_seq_number last_seq_for_cache; unsigned long lines_in_children; long line_offset; if (is_at_end_of_translation_unit_file(curr_file, seq_number)) { /* At end of source. Use the last line of the primary source file. */ *at_end_of_source = TRUE; seq_number--; } /* if */ lines_in_children = 0; /* See if the sequence number falls within any child file. */ examine_children: if (!physical_line) { /* #line directives are just as valid as #includes. */ lines_in_children = 0; } else { /* We want the physical line number, so #line directives count less than #includes. */ if (curr_file->full_name != NULL) { /* Examining a real file, rather than an entry for a #line directive. Remember the physical file information in case what we're descending to is an entry for a #line directive. */ phys_curr_file = curr_file; lines_in_children = 0; } /* if */ } /* if */ child_file = curr_file->first_child_file; /* Record the first sequence number of the current file as the first sequence number for which the cached information applies. This will be updated below if necessary to reflect child files. */ first_seq_for_cache = curr_file->first_seq_number; /* Check the sequence number against each child. The children are in order by sequence number. */ while (child_file != NULL) { if (seq_number < child_file->first_seq_number) { /* Sequence number falls before the start of this child, and therefore must be in the current file. */ break; } else if (!*at_end_of_source && seq_number <= child_file->last_seq_number) { /* The sequence number falls within this child (or one of its children). Don't search the child files when looking for the source file for the end-of-source file location. */ curr_file = child_file; goto examine_children; } /* if */ /* The sequence number falls after this child, so keep looking. Keep track of the number of lines in children. If the entry we are skipping over is for a #line directive, only count the lines in its #include children, not those of the current file spanned by the #line directive. */ if (child_file->full_name != NULL) { /* Real file. */ lines_in_children += child_file->last_seq_number - child_file->first_seq_number + 1; } else { /* #line directive. Note that typically when #line directives appear there are no #includes, so the loop here does nothing, and we skip over any grandchild files that represent #line directives. */ for (grandchild_file = child_file->first_child_file; grandchild_file != NULL && grandchild_file->full_name != NULL; grandchild_file = grandchild_file->next) { lines_in_children += grandchild_file->last_seq_number - grandchild_file->first_seq_number + 1; } /* for */ } /* if */ /* Record the sequence number following this child as the first sequence number for which the cached information applies. */ first_seq_for_cache = child_file->last_seq_number + 1; child_file = child_file->next; } /* while */ /* Save the current file entry before potentially changing it to point to the physical file. */ orig_curr_file = curr_file; if (physical_line) { /* If we want to ignore #line directives, go back to the last entry for a real file that we saw. */ check_assertion(phys_curr_file != NULL); curr_file = phys_curr_file; } /* if */ /* Save information about the file in which this sequence number was found so that subsequent lines may be found more quickly. */ line_offset = curr_file->first_line_number; line_offset -= (long)curr_file->first_seq_number; line_offset -= (long)lines_in_children; /* Save information about this conversion so that subsequent conversions can be done more quickly. */ seq_cache.first_seq_number = first_seq_for_cache; if (child_file != NULL) { /* This cache entry is valid up to the first line of the next child file. */ last_seq_for_cache = child_file->first_seq_number - 1; } else { /* This cached entry is valid through the end of the current file. */ last_seq_for_cache = orig_curr_file->last_seq_number; } /* if */ update_seq_cache(curr_file, first_seq_for_cache, last_seq_for_cache, line_offset, physical_line); /* Compute the line number to be returned to the caller. */ *line_number = seq_number + (a_line_number)line_offset; return curr_file; } /* find_seq_in_source_files */ a_source_file_ptr source_file_for_seq(a_seq_number seq_number, a_line_number *line_number, a_boolean *at_end_of_source, a_boolean physical_line) /* Find the source file entry within which the sequence number seq_number falls, and return a pointer to it. Return NULL if the sequence number falls outside of any file. If the sequence number falls within a file, also return *line_number set to the line number in the file. Return *at_end_of_source TRUE if the line number is the special number indicating the end-of-file line (one more than the last line in the primary input file). If physical_line is TRUE, ignore #line directive information and return the physical line position for the sequence number. */ { a_source_file_ptr curr_file = NULL; db_enter(5, "source_file_for_seq"); #if DEBUG if (debug_level >= 5) { db_source_file_seq_info(il_header.primary_source_file, 0); } /* if */ #endif /* DEBUG */ *at_end_of_source = FALSE; *line_number = 0; if (seq_number == 0 || il_header.primary_source_file == NULL) { /* Unknown position or no files. */ } else if (physical_line == seq_cache.physical_line && seq_number >= seq_cache.first_seq_number && seq_number <= seq_cache.last_seq_number) { /* If this sequence number falls into the range of lines associated with the information saved by the last lookup, use the information saved last time to speed up the conversion. */ *line_number = seq_number + (a_line_number)seq_cache.line_offset; curr_file = seq_cache.source_file; } else { if (physical_line || !okay_to_use_seq_number_lookup_table) { /* The less common case where #line directives are ignored. Use the more expensive search of the source file data structure. This is also used if the sequence number lookup table is not known to be in a valid state. */ curr_file = find_seq_in_source_files(seq_number, line_number, at_end_of_source, physical_line); } else { /* Use the sequence number lookup table to find the source file. */ curr_file = find_seq_in_lookup_table(seq_number, line_number, at_end_of_source); } /* if */ } /* if */ #if DEBUG if (debug_level >= 5 || db_flag_is_set("source_file_for_seq")) { fprintf(f_debug, "File=%s, Line=%lu, sequence number=%lu\n", curr_file == NULL ? "" : curr_file->file_name, (unsigned long)*line_number, (unsigned long)seq_number); } /* if */ #endif /* DEBUG */ db_exit(); return curr_file; } /* source_file_for_seq */ a_source_file_ptr conv_seq_to_file_and_line(a_seq_number seq_number, a_const_char **file_name, a_const_char **full_name, a_line_number *line_number, a_boolean *at_end_of_source) /* For the sequence number given by seq_number, find the corresponding file and line number. Return the short and long forms of the file name in *file_name and *full_name, and the line number in *line_number. In some cases *full_name can be NULL while *file_name is not (see a_source_file for more information about full vs file name). If the sequence number indicates the end-of-source line, the file names will be set to the primary source file, the line number to the last line in that file, and *at_end_of_source will be set TRUE (it is set to FALSE in all other cases). If the sequence number indicates an unknown position, the file names will be set to zero-length strings, and the line number to 0. Return the source file pointer associated with seq_number. */ { a_source_file_ptr proper_file; db_enter(5, "conv_seq_to_file_and_line"); /* Find out which file the sequence number is in. */ proper_file = source_file_for_seq(seq_number, line_number, at_end_of_source, /*physical_line=*/FALSE); if (proper_file == NULL) { /* Strange or unknown position. */ *file_name = *full_name = ""; *line_number = 0; } else { *file_name = proper_file->file_name; *full_name = proper_file->full_name; } /* if */ #if DEBUG if (debug_level >= 5) { if (*line_number == 0) { fprintf(f_debug, "seq %lu is outside of all source.\n", (unsigned long)seq_number); } else { fprintf(f_debug, "seq %lu maps into line %lu of file \"%s\".\n", (unsigned long)seq_number, (unsigned long)*line_number, *file_name); if (*at_end_of_source) fprintf(f_debug, "(really: at end of source)\n"); } /* if */ } /* if */ #endif /* DEBUG */ db_exit(); return proper_file; } /* conv_seq_to_file_and_line */ #if !STANDALONE_UTILITY_PROGRAM a_boolean same_source_file(a_source_position_ptr pos_1, a_source_position_ptr pos_2) /* Given two source positions, determine if they come from the same source file or not. Return TRUE if so, FALSE otherwise. */ { a_boolean result = FALSE; a_const_char *file_name_1, *full_name_1, *file_name_2, *full_name_2; a_line_number line_no; a_boolean eos; (void)conv_seq_to_file_and_line(pos_1->seq, &file_name_1, &full_name_1, &line_no, &eos); (void)conv_seq_to_file_and_line(pos_2->seq, &file_name_2, &full_name_2, &line_no, &eos); if (full_name_1 != NULL && full_name_2 != NULL && a_path_handle{full_name_1} == full_name_2) { result = TRUE; } /* if */ return result; } /* same_source_file */ #endif /* !STANDALONE_UTILITY_PROGRAM */ #if DEBUG a_line_number db_line_for_seq(a_seq_number seq_number) /* Return the line number that corresponds to the given sequence number. Also print the file name and line number. */ { a_const_char *file_name, *full_name; a_boolean at_end_of_source; a_line_number line_number; (void)conv_seq_to_file_and_line(seq_number, &file_name, &full_name, &line_number, &at_end_of_source); fprintf(f_debug, "\nfile %s, line %ld\n", file_name, (long)line_number); return line_number; } /* db_line_for_seq */ #endif /* DEBUG */ #if !STANDALONE_UTILITY_PROGRAM void conv_seq_to_physical_file_and_line(a_seq_number seq_number, a_source_file_ptr *src_file, a_line_number *physical_line, a_boolean *at_end_of_source) /* For the sequence number given by seq_number, find the corresponding physical file and line number. A pointer to the IL source file entry is returned in *src_file, and the physical line number is returned in *physical_line. If the sequence number indicates the end-of-source line, the source file pointer will be set to the primary source file, the line number to the last line in that file, and *at_end_of_source will be set TRUE (it is set to FALSE in all other cases). If the sequence number indicates an unknown position, the source file pointer will be set to NULL, and the line number to 0. */ { db_enter(5, "conv_seq_to_physical_file_and_line"); /* Find out which file the sequence number is in. */ *src_file = source_file_for_seq(seq_number, physical_line, at_end_of_source, /*physical_line=*/TRUE); db_exit(); } /* conv_seq_to_physical_file_and_line */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_source_file_ptr eff_primary_source_file(void) /* Return a pointer to the source file entry for the effective primary source file. This is usually the same as the primary source file, but is different when the source file begins with a #line directive; in that case, the primary source file is the one named in that #line directive. Save the value that has been computed so that it can be reused on subsequent calls. If there is no effective primary source file yet, return the actual primary source file. */ { a_source_file_ptr primary_file = il_header.primary_source_file; a_source_file_ptr first_file_under_primary; if (effective_primary_source_file == NULL) { if (primary_file != NULL) { first_file_under_primary = primary_file->first_child_file; while (first_file_under_primary != NULL && first_file_under_primary->included_by_preinclude) { /* Ignore files included by --preinclude. */ first_file_under_primary = first_file_under_primary->next; } /* if */ if (first_file_under_primary != NULL) { /* See if the first file entry under the primary file entry is for a #line directive that is the first line of the input and specifies a line number of 1. */ if (first_file_under_primary->full_name == NULL && first_file_under_primary->first_seq_number == 1+1 && first_file_under_primary->first_line_number == 1) { /* Yes -- this is the effective primary source file. */ effective_primary_source_file = first_file_under_primary; } else { effective_primary_source_file = primary_file; } /* if */ } /* if */ } /* if */ } /* if */ return effective_primary_source_file != NULL ? effective_primary_source_file : primary_file; } /* eff_primary_source_file */ #if !STANDALONE_UTILITY_PROGRAM a_boolean seq_is_in_include_file(a_seq_number seq_number) /* Return TRUE if the sequence number seq_number falls within an include file (or a C++/CLI assembly). */ { a_boolean in_include_file; a_source_file_ptr proper_file; a_line_number line_number; a_boolean at_end_of_source; proper_file = source_file_for_seq(seq_number, &line_number, &at_end_of_source, /*physical_line=*/FALSE); if (proper_file == NULL) { /* Sequence number is not in a file, so it's not in an include file. */ in_include_file = FALSE; } else { /* full_name will be NULL for source file entries associated with #line directives. When #line directives are present, compare the name of the file found with the name of the effective primary source file (the one named by the initial #line directive). If they don't match, assume we're in an include file. */ a_source_file_ptr primary_source_file; primary_source_file = eff_primary_source_file(); in_include_file = proper_file->is_include_file || (proper_file->full_name == NULL && strcmp(proper_file->file_name, primary_source_file->file_name) != 0); } /* if */ return in_include_file; } /* seq_is_in_include_file */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_boolean seq_is_in_system_header(a_seq_number seq_number) /* Return TRUE if the given sequence number falls within a system header. */ { a_source_file_ptr sfp; a_boolean at_end_of_source; a_line_number line_number; sfp = source_file_for_seq(seq_number, &line_number, &at_end_of_source, /*physical_line=*/FALSE); return sfp != NULL && sfp->from_system_include_dir; } /* seq_is_in_system_header */ #if ORPHAN_PROCESSING_NEEDED /* Record a file-scope entry as a potential orphan. The macro here ensures that once the entry is placed on an orphan list the subroutine is no longer called (well, except if it's the last entry on the list). The orphan is recorded in the current translation unit. */ #define add_orphaned_file_scope_il_entry(entry_ptr, entry_kind) \ { if (fs_orphan_pointer_of(entry_ptr) == NULL) { \ f_add_orphaned_file_scope_il_entry((entry_ptr), (entry_kind), \ curr_translation_unit); \ } /* if */ \ } /* add_orphaned_file_scope_il_entry */ static void f_add_orphaned_file_scope_il_entry( char *entry_ptr, an_il_entry_kind entry_kind, a_translation_unit_ptr tup) /* Link the specified file scope IL entry onto the orphaned_file_scope_il_entries linked list for the designated IL entry kind in the designated translation unit. Only IL entries in the file scope memory region have the necessary additional pointer space allocated immediately preceding the entry. The caller must check that fs_orphan_pointer_of(entry_ptr) == NULL for speed. */ { an_orphaned_il_entry_list *orphan_array; char **last_entry_ptr; check_assertion_str(in_file_scope(entry_ptr), "f_add_orphaned_file_scope_...: IL entry not in file scope"); #if !STANDALONE_UTILITY_PROGRAM if (in_front_end) { check_assertion_str(tup != NULL, "f_add_orphaned_...: tup NULL"); check_assertion_str((tup == translation_units) == !in_secondary_trans_unit(entry_ptr), "f_add_orphaned_file_scope_il_entry: wrong translation unit"); orphan_array = tup->orphaned_file_scope_il_entries; } else #endif /* !STANDALONE_UTILITY_PROGRAM */ /* Do not insert code here. */ { /* Use the global variable. */ orphan_array = orphaned_file_scope_il_entries; } /* if */ /* Check if this IL entry is already on the orphaned entry list. */ last_entry_ptr = &orphan_array[(int)entry_kind].last_entry; /* The following check was done by the macro that guards entry to this routine: fs_orphan_pointer_of(entry_ptr) == NULL. If one wants this routine to be directly callable, the test should be done again here. */ if (entry_ptr != *last_entry_ptr) { /* This entry is not in the existing list; add it to the end of the list. */ if (*last_entry_ptr == NULL) { /* This is the first entry on this list */ orphan_array[(int)entry_kind].first_entry = entry_ptr; } else { /* Add to the tail of the existing list. */ fs_orphan_pointer_of(*last_entry_ptr) = entry_ptr; } /* if */ *last_entry_ptr = entry_ptr; } /* if */ } /* f_add_orphaned_file_scope_il_entry */ void f_possibly_add_orphaned_file_scope_il_entry( char *entry_ptr, an_il_entry_kind entry_kind, a_translation_unit_ptr tup) /* Interface to add_orphaned_file_scope_il_entry. Adds the indicated entry (of the indicated kind) to the orphan lists of the indicated translation unit. However, detects certain cases that cannot be orphans (e.g., class members), and does not enter those. */ { check_assertion_str(in_file_scope(entry_ptr), "f_possibly_add_orphaned_file_scope_...: IL entry not in file scope"); /* Do not put the entry on a list if it's already on a list. */ if (fs_orphan_pointer_of(entry_ptr) == NULL) { a_boolean could_be_orphan = TRUE; a_boolean do_source_corresp_check = FALSE; switch (entry_kind) { case iek_constant: do_source_corresp_check = TRUE; { a_constant_ptr con = (a_constant_ptr)entry_ptr; if (con->kind == (a_constant_repr_kind)ck_template_param) { /* Template parameter constants can be orphans even if they are named. */ do_source_corresp_check = FALSE; } else if (con->kind == (a_constant_repr_kind)ck_integer && !con->is_named_constant_definition) { /* Uses of named enumeration constants result in ck_integer entries with a non-NULL "name", but they can be orphans. */ do_source_corresp_check = FALSE; } /* if */ } break; case iek_type: do_source_corresp_check = TRUE; { a_type_ptr type = (a_type_ptr)entry_ptr; if (is_immediate_class_type(type) || is_immediate_enum_type(type)) { /* Classes and enums cannot be orphans. */ could_be_orphan = FALSE; } else if (type->kind == (a_type_kind)tk_template_param) { /* Template parameter types can be orphans even if they are named. */ do_source_corresp_check = FALSE; } else if (type->kind == (a_type_kind)tk_routine) { if (type->variant.routine.extra_info->assoc_routine != NULL) { /* A routine type entry associated with a specific routine cannot be an orphan since the routine entry will be in file scope memory also. */ could_be_orphan = FALSE; } /* if */ } /* if */ } break; case iek_variable: case iek_routine: case iek_namespace: case iek_field: case iek_template: case iek_based_type_list_member: /* Things that should always be on lists. */ could_be_orphan = FALSE; break; #if C99_IL_EXTENSIONS_SUPPORTED case iek_internal_complex_value: could_be_orphan = TRUE; break; #endif /* C99_IL_EXTENSIONS_SUPPORTED */ case iek_base_class: /* Dependent base classes in prototype instantiations can be orphans. Also, placeholder base class entries for prototype instantiations of mem-initializers can be orphans (bcp->derived_class is NULL for those). */ { a_base_class_ptr bcp = (a_base_class_ptr)entry_ptr; could_be_orphan = FALSE; if (bcp->type->variant.class_struct_union.is_nonreal_class || bcp->derived_class == NULL) { could_be_orphan = TRUE; } /* if */ } break; case iek_name_reference: could_be_orphan = TRUE; break; case iek_template_arg: /* A tpck_template_ref constant might be allocated in function scope and pointing to an entry of type a_template_arg (in file scope). */ could_be_orphan = TRUE; break; case iek_attribute: could_be_orphan = TRUE; break; case iek_param_type: /* Parameters of requires-expressions can be orphaned. */ could_be_orphan = TRUE; break; case iek_token_sequence: case iek_data_member_spec: /* A token sequence or data-member spec can be reached only through a ck_reflection constant and has no source correspondence, so it can be an orphan (like the other entity kinds above). */ could_be_orphan = TRUE; break; default: could_be_orphan = FALSE; break; } /* switch */ if (do_source_corresp_check && could_be_orphan) { a_source_correspondence *scp = (a_source_correspondence *)entry_ptr; if (scp->name != NULL || scp_is_class_or_namespace_member(scp)) { /* Named entities cannot be orphans. */ /* Class and namespace members cannot be orphans. */ could_be_orphan = FALSE; } /* if */ } /* if */ if (could_be_orphan) { f_add_orphaned_file_scope_il_entry(entry_ptr, entry_kind, tup); } /* if */ } /* if */ } /* f_possibly_add_orphaned_file_scope_il_entry */ #endif /* ORPHAN_PROCESSING_NEEDED */ #if SCOPE_ORPHANED_LIST_PROCESSING_NEEDED #if !STANDALONE_UTILITY_PROGRAM static void r_add_scope_orphaned_il_lists(a_scope_ptr scope, a_routine_ptr routine) /* If the indicated scope (a function or block scope) contains non-empty lists that are in the file scope memory region (e.g., local types or static variables), create a_scope_orphaned_list_header entry to hold those pointers in the file scope so that orphan processing can be done on the lists later. Also use recursion to visit all block scopes under this scope to do the same processing. routine indicates the function this scope is part of. This routine does the recursive processing for add_scope_orphaned_il_lists. */ { a_type_ptr types = scope->types; a_variable_ptr variables = scope->variables; a_namespace_ptr namespaces = scope->namespaces; #if GENERATE_SOURCE_SEQUENCE_LISTS a_src_seq_sublist_ptr sublists = scope->src_seq_sublist_list; #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ a_scope_ptr block_scope; if (types != NULL || variables != NULL || #if GENERATE_SOURCE_SEQUENCE_LISTS sublists != NULL || #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ namespaces != NULL) { /* At least one of the IL pointers is not NULL; create a_scope_orphaned_list_header in the file scope region and add it to the list headed by il_header.scope_orphaned_list_headers. */ a_scope_orphaned_list_header_ptr solhp; solhp = alloc_scope_orphaned_list_header(routine, scope->number); solhp->assoc_routine = routine; solhp->scope_number = scope->number; solhp->orphaned_types = types; solhp->orphaned_variables = variables; solhp->orphaned_namespaces = namespaces; #if GENERATE_SOURCE_SEQUENCE_LISTS solhp->orphaned_src_seq_sublists = sublists; #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ solhp->next = NULL; if (il_header.scope_orphaned_list_headers == NULL) { il_header.scope_orphaned_list_headers = solhp; } else { curr_translation_unit->last_scope_orphaned_list_header->next = solhp; } /* if */ curr_translation_unit->last_scope_orphaned_list_header = solhp; scope->scope_orphaned_list_header_generated = TRUE; } /* if */ /* Process subscopes of this scope. */ for (block_scope = scope->scopes; block_scope != NULL; block_scope = block_scope->next) { r_add_scope_orphaned_il_lists(block_scope, routine); } /* for */ } /* r_add_scope_orphaned_il_lists */ void add_scope_orphaned_il_lists(a_scope_ptr scope) /* If the indicated scope (a function scope) contains non-empty lists that are in the file scope memory region (e.g., local types or static variables), create a_scope_orphaned_list_header entry to hold those pointers in the file scope so that orphan processing can be done on the lists later. Also visit all block scopes attached to this scope and do the same processing. */ { r_add_scope_orphaned_il_lists(scope, scope->variant.routine.ptr); } /* add_scope_orphaned_il_lists */ #endif /* !STANDALONE_UTILITY_PROGRAM */ #endif /* SCOPE_ORPHANED_LIST_PROCESSING_NEEDED */ #if !STANDALONE_UTILITY_PROGRAM /* Entry used to record the original and copy addresses for particular entities of interest handled by a tree copy (e.g., i_copy_dynamic_init), so that later references to the original entry can be remapped to the copy. */ typedef struct a_copy_remap_entry *a_copy_remap_entry_ptr; typedef struct a_copy_remap_entry { a_copy_remap_entry_ptr next; /* Next entry on the list. */ char *original; char *copy; /* Original and copy addresses. */ } a_copy_remap_entry; STATIC_THREAD a_copy_remap_entry_ptr avail_copy_remap_entries; /* Copy remap entries freed and available for reuse. */ #if DEBUG STATIC_THREAD unsigned long num_copy_remap_entries_allocated; #endif /* DEBUG */ /* Control block used to pass around information during tree copies, e.g., copy_expr_tree. */ typedef struct a_tree_copy_control_block { a_copy_remap_entry_ptr remapped_entries; /* Selective list of remapped entries, used to remap references to those entries found later in the same copy. */ a_boolean inlining_failed; /* TRUE if, during the inlining of an expression tree, a problem is found that prevents inlining. */ } a_tree_copy_control_block; static void add_copy_remap_entry(char *original, char *copy, a_tree_copy_control_block *cblock) /* Allocate a copy remap entry and initialize it. original and copy are the addresses of the original entry and the copy made from it. Add the entry to the remapped_entries list of the control block cblock. */ { a_copy_remap_entry_ptr crep; if (avail_copy_remap_entries != NULL) { /* Reuse a previously-freed entry. */ crep = avail_copy_remap_entries; avail_copy_remap_entries = crep->next; } else { /* Allocate a new entry. */ crep = (a_copy_remap_entry_ptr)alloc_fe(sizeof(a_copy_remap_entry)); #if DEBUG num_copy_remap_entries_allocated++; #endif /* DEBUG */ } /* if */ /* Clear the entry and add it to the front of the remapped_entries list. */ crep->next = cblock->remapped_entries; cblock->remapped_entries = crep; crep->original = original; crep->copy = copy; } /* add_copy_remap_entry */ static void free_copy_remap_entry_list(a_copy_remap_entry_ptr crep) /* Free a list of copy remap entries and make them available for reuse. */ { a_copy_remap_entry_ptr crep_tail; if (crep != NULL) { /* Find the last entry on the list. */ crep_tail = crep; while (crep_tail->next != NULL) crep_tail = crep_tail->next; /* Add the current available list to the end of the list passed in by the caller. */ crep_tail->next = avail_copy_remap_entries; avail_copy_remap_entries = crep; } /* if */ } /* free_copy_remap_entry_list */ static char *find_copy_remap_address(char *original, a_tree_copy_control_block *cblock) /* Find the remap entry on the remapped_entries list of cblock that has the indicated original address, and return the copy address for it. If no such entry is found, return NULL. */ { a_copy_remap_entry_ptr crep; char *copy = NULL; for (crep = cblock->remapped_entries; crep != NULL; crep = crep->next) { if (crep->original == original) { copy = crep->copy; break; } /* if */ } /* for */ return copy; } /* find_copy_remap_address */ static void clear_tree_copy_control_block(a_tree_copy_control_block *cblock) /* Set the fields of a tree copy control block to default values. */ { cblock->remapped_entries = NULL; cblock->inlining_failed = FALSE; } /* clear_tree_copy_control_block */ static void done_with_tree_copy_control_block( a_tree_copy_control_block *cblock) /* We've finished with a tree copy whose control block was cblock. Clean up whatever needs it in the control block. */ { free_copy_remap_entry_list(cblock->remapped_entries); } /* done_with_tree_copy_control_block */ /* Forward declarations needed because of mutual recursion: */ static a_dynamic_init_ptr i_copy_dynamic_init( a_dynamic_init_ptr dip, an_expr_copy_options_set options, a_tree_copy_control_block *cblock); static an_expr_node_ptr i_copy_list_of_expr_trees( an_expr_node_ptr expr_list, an_expr_copy_options_set options, a_tree_copy_control_block *cblock); static an_expr_node_ptr i_copy_expr_tree(an_expr_node_ptr expr, an_expr_copy_options_set options, a_tree_copy_control_block *cblock); void clear_local_scope_ref_if_present(a_source_correspondence *scp) /* If the entity with the indicated source correspondence has an associated a_local_scope_ref entry (because of a memory region problem in pointing to the parent scope), delete it. */ { if (scp->parent_via_local_scope_ref) { /* Find and remove the associated a_local_scope_ref entry. */ check_assertion(scp->enclosing_routine != NULL); { a_scope_ptr enclosing_fn_scope = scope_for_routine(scp->enclosing_routine); a_local_scope_ref_ptr ref, prev_ref; for (prev_ref = NULL, ref = enclosing_fn_scope->scope_refs; ; prev_ref = ref, ref = ref->next) { check_assertion_str(ref != NULL, "clear_parent: local scope ref entry not found"); if (ref->referrer.ptr == (char *)scp) break; } /* for */ if (prev_ref == NULL) { enclosing_fn_scope->scope_refs = ref->next; } else { prev_ref->next = ref->next; } /* if */ } scp->parent_via_local_scope_ref = FALSE; } /* if */ } /* clear_local_scope_ref_if_present */ void clear_parent(a_source_correspondence *scp) /* Clear parent scope and class/namespace membership information from the indicated source correspondence. The parent pointer is set to point to the file scope, unless it was NULL, in which case it is left alone. */ { clear_local_scope_ref_if_present(scp); scp->is_class_member = FALSE; scp->is_local_to_function = FALSE; scp->enclosing_routine = NULL; if (scp->parent_scope != NULL) { scp->parent_scope = il_header.primary_scope; } /* if */ } /* clear_parent */ void set_parent_scope(a_source_correspondence *scp, an_il_entry_kind entry_kind, a_scope_ptr parent_scope) /* Set the parent scope of the entity whose source correspondence is given by scp, and whose kind is entry_kind, to parent_scope. If there's a memory region problem, allocate a_local_scope_ref entry to handle it. If the parent scope is not currently on the scope stack, innermost_function_scope must be set correctly. */ { check_assertion(parent_scope != NULL && scp->parent_scope == NULL && !scp->parent_via_local_scope_ref); scp->parent_scope = parent_scope; if (!in_file_scope(parent_scope)) { /* The entity appears in a function-local scope or, possibly, a GNU statement expression appearing in a nonlocal context. */ a_scope_ptr func_scope; a_scope_depth depth = parent_scope->depth_in_scope_stack; if (depth != NO_SCOPE_DEPTH) { /* The parent scope is on the scope stack, so get its enclosing routine from the stack. */ depth = scope_stack[depth].depth_innermost_function_scope; if (depth == NO_SCOPE_DEPTH) { /* This can happen with a GNU statement expression appearing in a nonlocal context. */ check_assertion(gnu_mode); goto done; } /* if */ func_scope = scope_stack[depth].il_scope; check_assertion(func_scope != NULL && scope_is(func_scope, sck_function)); } else { /* The parent scope is not on the scope stack: Use innermost_function_scope if available; otherwise, iterate up the scope chain. */ if (innermost_function_scope != NULL) { func_scope = innermost_function_scope; } else { func_scope = parent_scope; while (!scope_is(func_scope, sck_function)) { func_scope = func_scope->parent; } /* while */ } /* if */ } /* if */ scp->enclosing_routine = func_scope->variant.routine.ptr; if (in_file_scope(scp)) { /* There is a memory region problem; scp (in the file scope memory region) can't point directly to parent_scope (in a function scope memory region). */ /* Record a local-scope-ref entry. */ make_local_scope_ref(parent_scope, (char*)scp, entry_kind, func_scope); scp->parent_scope = NULL; scp->parent_via_local_scope_ref = TRUE; } /* if */ } /* if */ done:; } /* set_parent_scope */ void add_to_namespaces_list(a_namespace_ptr nsp) /* Add the given namespace entry to the innermost namespace scope, which will be either the file scope or a namespace scope. */ { a_scope_stack_entry_ptr ssep; a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; ssep = &scope_stack[depth_innermost_namespace_scope]; sp = ensure_il_scope_exists(ssep); pointers_block = assoc_pointers_block_of(ssep); if (sp->namespaces == NULL) { sp->namespaces = nsp; } else { pointers_block->last_namespace->next = nsp; } /* if */ pointers_block->last_namespace = nsp; if (parent_scope_of(nsp) == NULL) { set_parent_scope(&nsp->source_corresp, iek_namespace, sp); } /* if */ } /* add_to_namespaces_list */ void add_to_using_declarations_list(a_using_decl_ptr udp, a_scope_depth depth) /* Add the given using-decl entry to the using_declarations list for the scope specified by depth. */ { a_scope_stack_entry_ptr ssep; a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; assert_is_valid_scope_depth(depth); ssep = &scope_stack[depth]; sp = ensure_il_scope_exists(ssep); pointers_block = assoc_pointers_block_of(ssep); if (sp->using_declarations == NULL) { sp->using_declarations = udp; } else { pointers_block->last_using_declaration->next = udp; } /* if */ pointers_block->last_using_declaration = udp; } /* add_to_using_declarations_list */ void add_to_using_directives_list(a_using_decl_ptr udp, a_scope_depth depth) /* Add the given using-decl entry to the using_directives list for the scope specified by depth. */ { a_scope_stack_entry_ptr ssep; a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; assert_is_valid_scope_depth(depth); ssep = &scope_stack[depth]; sp = ensure_il_scope_exists(ssep); pointers_block = assoc_pointers_block_of(ssep); if (sp->using_directives == NULL) { sp->using_directives = udp; } else { pointers_block->last_using_directive->next = udp; } /* if */ pointers_block->last_using_directive = udp; } /* add_to_using_directives_list */ static void add_to_scopes_list(a_scope_ptr scope_ptr, a_scope_stack_entry_ptr ssep) /* Add the given IL scope to the scopes list for the scope stack entry pointed to by ssep. */ { /* The list of scope entries is maintained using the first_scope/last_scope pointers in the scope stack entry. This is because there may not be an allocated IL scope at this point. When the scope stack is popped, the list of scopes will be transferred to the IL scope entry if there is one; otherwise, it will be added to the scopes list for the parent scope. This is done so that unneeded empty scopes are not allocated even when they are sandwiched between non-empty scopes. */ if (ssep->first_scope == NULL) { ssep->first_scope = scope_ptr; } else { scope_ptr->prev = ssep->last_scope->prev; ssep->last_scope->next = scope_ptr; } /* if */ ssep->last_scope = scope_ptr; scope_ptr->next = NULL; } /* add_to_scopes_list */ void break_instance_source_corresp(ARG_UNUSED a_source_correspondence *sc) /* Clear any parts of the indicated source correspondence that record information related to a specific source occurrence of an entity. Do not clear information related to the identity of the entity. So, for example, if this is applied to a copy of a constant, the copy is another use of the same constant, rather than a new constant that has the same value. */ { #if GENERATE_SOURCE_SEQUENCE_LISTS sc->source_sequence_entry = NULL; #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ #if EXTRA_SOURCE_POSITIONS_IN_IL sc->decl_pos_info = NULL; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ #if ONE_INSTANTIATION_PER_OBJECT sc->per_instantiation_needed_flags = NULL; #endif /* ONE_INSTANTIATION_PER_OBJECT */ sc->name_references = NULL; #if MAINTAIN_NEEDED_FLAGS reset_needed_flag(sc); #endif /* MAINTAIN_NEEDED_FLAGS */ } /* break_instance_source_corresp */ static void break_source_corresp_full(a_source_correspondence *sc, a_boolean keep_name) /* Clear any parts of the indicated source correspondence that record information related to a specific source occurrence of an entity, or the name or parent of the entity. So, for example, if this is applied to a copy of a constant, the copy is a distinct constant that has the same value as the original constant. If keep_name is TRUE, leave the sc->name field unchanged. */ { sc->assoc_info = NULL; if (!keep_name) { sc->name = NULL; } /* if */ sc->trans_unit_corresp = NULL; sc->is_class_member = FALSE; sc->parent_scope = NULL; sc->access = (an_access_specifier)as_public; #if MICROSOFT_EXTENSIONS_ALLOWED sc->microsoft_identifier_used = FALSE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if NEED_NAME_MANGLING sc->unmangled_name_or_mangled_encoding = NULL; sc->name_has_been_mangled = FALSE; sc->mangled_name_cannot_be_included_in_other_name = FALSE; sc->final_name_mangling_pending = FALSE; sc->unnamed_entity_given_fabricated_name = FALSE; #endif /* NEED_NAME_MANGLING */ break_instance_source_corresp(sc); } /* break_source_corresp_full */ void break_source_corresp(a_source_correspondence *sc) /* Clear any parts of the indicated source correspondence that record information related to a specific source occurrence of an entity, including the name and parent of the entity (if any). */ { break_source_corresp_full(sc, /*keep_name=*/FALSE); } /* break_source_corresp */ void break_constant_source_corresp(a_constant_ptr cp) /* Break the correspondence between the given constant and any particular source occurrence of the constant. The altered constant is a distinct constant with the same value as the original constant, and not simply another use of the same constant. The name of the constant, if any, is kept. */ { a_boolean break_instance = FALSE; if (cp->kind == (a_constant_repr_kind)ck_template_param) { /* For certain template constants, keep the information that defines the identity of the constant. */ a_template_param_constant_kind kind = cp->variant.template_param.kind; if (kind == (a_template_param_constant_kind)tpck_param || kind == (a_template_param_constant_kind)tpck_member || kind == (a_template_param_constant_kind)tpck_destructor || kind == (a_template_param_constant_kind)tpck_unknown_function) { break_instance = TRUE; if (kind == (a_template_param_constant_kind)tpck_param) { cp->source_corresp.parent_scope = NULL; } /* if */ } /* if */ } /* if */ if (break_instance) { break_instance_source_corresp(&cp->source_corresp); /* Don't allow more than one constant with the same associated symbol. Make an exception for tpck_member constants whose symbol may be needed during substitution. */ if (constant_is(cp, ck_template_param) && tpck_is(cp, tpck_member)) { /* Keep the associated symbol. */ } else { cp->source_corresp.assoc_info = NULL; } /* if */ } else { /* If the original expression has a name but no backing expression, keep its name. This is useful, for example, to be able to render the original form of the constant in the C++-generating back end. (Don't do this if there is a backing expression to avoid IL loops.) */ break_source_corresp_full(&cp->source_corresp, /*keep_name=*/cp->expr == NULL); } /* if */ /* Make sure that this reference is not considered the definition of a named constant. */ cp->is_named_constant_definition = FALSE; } /* break_constant_source_corresp */ void fix_memory_region_problems_in_copied_constant(a_constant_ptr cp) /* The indicated allocated constant has just been created by copying from another constant. If there are any memory region problems in the constant, fix them. */ { if (in_file_scope(cp)) { if (cp->expr != NULL && !in_file_scope(cp->expr)) { /* A constant in the file scope memory region has an attached backing expression in a function scope memory region. A direct pointer would violate the rule that file scope memory must not point into function scope memory. Record an indirect lerk_constant_expr reference in the enclosing function scope so expr_node_from_constant can still recover the expression. If no enclosing function scope is reachable, fall back to discarding the expression. Don't do this for calls from lowering because lowering will prune the backing expression causing the indirect reference to dangle. */ a_routine_ptr rp; a_scope_ptr sp = NULL; a_boolean lowering = FALSE; #if DO_IL_LOWERING lowering = il_lowering_underway; #endif /* DO_IL_LOWERING */ if (!lowering) { sp = get_innermost_function_scope(); if (cp->expr != NULL && expr_has_reference_to_routine_scope_variable(cp->expr)) { /* The backing expression names a variable local to a function that could be different from the current function (e.g., when a nontype template argument value carrying such a reference is substituted while instantiating an unrelated function). So obtain the scope to refer to from the variable. */ a_routine_ptr var_rp = get_routine_scope_variable_found() ->source_corresp.enclosing_routine; if (var_rp != NULL) { a_scope_ptr var_scope = scope_for_routine_or_null(var_rp); if (var_scope != NULL) sp = var_scope; } /* if */ } /* if */ if (sp == NULL) { rp = cp->source_corresp.enclosing_routine; if (rp != NULL) sp = scope_for_routine_or_null(rp); } /* if */ } /* if */ if (sp != NULL) { an_expr_node_ptr expr = cp->expr; cp->expr = NULL; if (mixed_regions_in_expr_tree(expr)) { /* The backing expression tree spans both the file scope memory region and a function scope memory region. Anonymous expr nodes left in the file scope region would be reachable only through this function-scope tree, and expr nodes cannot be recorded as file-scope orphans, so they would be lost on IL write-read. Copy the whole tree into the function scope memory region so that it is self-contained. */ a_memory_region_number save_region = curr_il_region_number; switch_il_region(mem_region_for_routine(sp->variant.routine.ptr)); expr = copy_expr_tree(expr, CE_COPYING_FOR_LOCAL_EXPR_NODE_REF); switch_il_region(save_region); } /* if */ make_local_expr_node_ref(expr, lerk_constant_expr, (char*)cp, sp); } else { cp->expr = NULL; } /* if */ } /* if */ if (cp->kind == ck_template_param) { a_template_param_constant_kind kind = cp->variant.template_param.kind; if (kind == tpck_sizeof || kind == tpck_datasizeof || kind == tpck_alignof || kind == tpck_uuidof || kind == tpck_typeid || kind == tpck_noexcept || kind == tpck_expression) { /* If a constant in the file scope memory region has an attached expression in a function scope memory region, break the link to the expression. In configurations that record prototype instantiations in the IL, an entry of type a_local_expr_node_ref is recorded so that the expression can be recovered using the function find_local_expr_node. */ an_expr_node_ptr *expr; if (kind == tpck_expression) { expr = &cp->variant.template_param.variant.expr; } else { expr = &cp->variant.template_param.variant.templ_sizeof.expr; } /* if */ if (*expr != NULL && !in_file_scope(*expr)) { #if PROTOTYPE_INSTANTIATIONS_IN_IL a_routine_ptr rp = cp->source_corresp.enclosing_routine; a_scope_ptr sp = NULL; if (rp != NULL) { sp = scope_for_routine_or_null(rp); } /* if */ if (sp == NULL) { sp = get_innermost_function_scope(); } /* if */ if (sp != NULL) { make_local_expr_node_ref( *expr, (kind == tpck_expression) ? lerk_tpl_param_expr : lerk_generic_sizeof, (char*)cp, sp); *expr = NULL; } else { /* Need to copy the expression (most likely a sub-expression of a function-local requires expression). */ *expr = copy_expr_tree(*expr, expr_stack->possible_rescan_context ? CE_PRESERVE_RESCAN_INFO : CE_NO_OPTIONS); } /* if */ #else /* !PROTOTYPE_INSTANTIATIONS_IN_IL */ *expr = NULL; #endif /* PROTOTYPE_INSTANTIATIONS_IN_IL */ } /* if */ } /* if */ } else if (cp->kind == ck_reflection) { if (cp->variant.reflection.entity.ptr != NULL && !in_file_scope(cp->variant.reflection.entity.ptr)) { extract_reflected_entity(&cp->variant.reflection); } /* if */ #if DO_IL_LOWERING && ASSIGN_STRING_LITERAL_SEQUENCE_NUMBERS } else if (cp->kind == ck_string) { /* If a string literal with an assigned sequence number is copied to the file scope, the sequence number no longer applies. */ cp->variant.string.sequence_number = 0; #endif /* DO_IL_LOWERING && ASSIGN_STRING_LITERAL_SEQUENCE_NUMBERS */ } /* if */ } /* if */ } /* fix_memory_region_problems_in_copied_constant */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_source_correspondence *source_corresp_for_il_entry( char *entity_ptr, an_il_entry_kind kind) /* Given an IL entry pointer (entity_ptr) of a given kind (kind), return a pointer to its source correspondence entry if it has one and NULL if it does not. */ { a_source_correspondence *scp; switch (kind) { case iek_constant: case iek_type: case iek_variable: case iek_field: case iek_routine: case iek_asm_entry: case iek_label: case iek_namespace: case iek_template: #if RECORD_MACROS_IN_IL case iek_macro: #endif /* RECORD_MACROS_IN_IL */ case iek_template_parameter: scp = &((a_constant_ptr)entity_ptr)->source_corresp; break; default: scp = NULL; } /* switch */ return scp; } /* source_corresp_for_il_entry */ a_boolean is_defined(char *entity_ptr, an_il_entry_kind kind) /* Given an IL entry pointer (entity_ptr) of a given kind (kind), return TRUE if the entity is defined; otherwise, return FALSE. */ { a_boolean result = FALSE; switch (kind) { case iek_template: { a_template_ptr tmpl = (a_template_ptr)entity_ptr; result = tmpl->canonical_template->definition_template != NULL; } break; case iek_type: { a_type_ptr ty = (a_type_ptr)entity_ptr; result = !ty->incomplete; } break; case iek_routine: { a_routine_ptr rp = (a_routine_ptr)entity_ptr; result = rp->defined; } break; case iek_variable: { a_variable_ptr vp = (a_variable_ptr)entity_ptr; result = vp->storage_class != sc_extern; } break; default: break; } /* switch */ return result; } /* is_defined */ a_boolean is_template_param_cast_constant(a_constant_ptr con, a_constant_ptr *p_base_con, a_boolean *is_explicit) /* Return TRUE if the given constant is a ck_template_param/tpck_expression entry for an eok_cast/eok_ref_cast node on top of an enk_constant node. If so, set *p_base_con to the underlying constant entry of that node, and set *is_explicit to TRUE or FALSE depending on whether the cast was explicit or not. */ { a_boolean result = FALSE; if (constant_is(con, ck_template_param) && tpck_is(con, tpck_expression) && con->variant.template_param.do_not_rescan) { an_expr_node_ptr expr = expr_node_from_tpck_expression(con); if (is_operation_node(expr) && (node_operator_is(expr, eok_cast) || node_operator_is(expr, eok_ref_cast)) && !expr->variant.operation.is_reinterpret_cast && !expr->variant.operation.is_const_cast && !(type_is(expr->type, tk_template_param) && expr->type->variant.template_param.kind == (a_template_param_type_kind)tptk_unknown)) { an_expr_node_ptr opnd = expr->variant.operation.operands; if (is_constant_node(opnd)) { *is_explicit = !expr->compiler_generated; *p_base_con = node_constant(opnd); result = TRUE; } /* if */ } /* if */ } /* if */ return result; } /* is_template_param_cast_constant */ a_boolean is_zero_constant(a_constant *constant) /* Return TRUE if the constant is an integer, fixed-point, or floating zero. */ { a_boolean is_zero = FALSE; a_float_kind float_kind; if (constant->kind == (a_constant_repr_kind)ck_integer && !constant->implicit_cast) { is_zero = (cmplit_integer_constant(constant, (a_host_large_integer)0) == 0); #if FIXED_POINT_ALLOWED } else if (constant->kind == (a_constant_repr_kind)ck_fixed_point) { is_zero = fxp_value_is_zero(&constant->variant.fixed_point_value); #endif /* FIXED_POINT_ALLOWED */ } else if (constant->kind == (a_constant_repr_kind)ck_float #if C99_IL_EXTENSIONS_SUPPORTED || constant->kind == (a_constant_repr_kind)ck_imaginary #endif /* C99_IL_EXTENSIONS_SUPPORTED */ ) { float_kind = skip_typerefs(constant->type)->variant.float_kind; is_zero = fp_is_zero_constant(float_kind, &constant->variant.float_value); #if C99_IL_EXTENSIONS_SUPPORTED } else if (constant->kind == (a_constant_repr_kind)ck_complex) { float_kind = skip_typerefs(constant->type)->variant.float_kind; is_zero = fp_is_zero_constant(float_kind, &constant->variant.complex_value->real) && fp_is_zero_constant(float_kind, &constant->variant.complex_value->imag); #endif /* C99_IL_EXTENSIONS_SUPPORTED */ #if GNU_EXTENSIONS_ALLOWED } else if (constant->kind == (a_constant_repr_kind)ck_label_difference) { a_constant_ptr from = constant->variant.label_difference.from_address; a_constant_ptr to = constant->variant.label_difference.to_address; is_zero = constant_is_address_of_label(from) && constant_is_address_of_label(to) && from->variant.address.variant.label == to->variant.address.variant.label; #endif /* GNU_EXTENSIONS_ALLOWED */ } /* if */ return is_zero; } /* is_zero_constant */ #if !STANDALONE_UTILITY_PROGRAM void set_error_constant(a_constant *cp) /* Set the indicated constant to make it an error constant. Such a constant is used for cases where there is an error, and a valid constant cannot be produced. */ { clear_constant(cp, (a_constant_repr_kind)ck_error); cp->type = error_type(); } /* set_error_constant */ a_constant_ptr alloc_error_constant(void) /* Allocate and return an error constant. The constant is allocated in the file scope and is unshared. */ { a_constant_ptr cp; cp = fs_constant((a_constant_repr_kind)ck_error); set_error_constant(cp); return cp; } /* alloc_error_constant */ a_dynamic_init_ptr make_error_constant_dynamic_init(void) /* Return a dynamic init entry for an error constant. */ { a_dynamic_init_ptr dip = alloc_dynamic_init((a_dynamic_init_kind)dik_constant); dip->variant.constant.ptr = alloc_error_constant(); return dip; } /* make_error_constant_dynamic_init */ static a_boolean is_context_where_instance_is_needed(void) /* Return TRUE if we are in a "real" context where a reference to a function or variable should result in the instantiation of the entity. This is FALSE in template-dependent contexts and discarded statements of constexpr if statements. */ { a_boolean result = TRUE; if (is_prototype_instantiation_context() || is_template_declaration_context()) { /* Do not instantiate things referenced from contexts such as prototype instantiations. */ result = FALSE; } else if (in_constexpr_if_discarded_statement()) { /* Do not instantiate things in the discarded branch of a constexpr if. */ result = FALSE; } /* if */ return result; } /* is_context_where_instance_is_needed */ void set_routine_address_constant(a_routine_ptr routine, a_constant *con, a_boolean set_address_taken_flag) /* Fill in the constant "con" as a ck_address constant for the address of the indicated routine. If set_address_taken_flag is TRUE, the address_taken flag in the routine is set (it's not always set because the constant might be used in a way that doesn't really take the address of the routine, e.g., a call). */ { clear_constant(con, (a_constant_repr_kind)ck_address); con->variant.address.kind = (an_address_base_kind)abk_routine; con->variant.address.variant.routine = routine; con->type = make_pointer_type(routine->type); if (set_address_taken_flag) routine->address_taken = TRUE; } /* set_routine_address_constant */ void set_variable_address_taken(a_variable_ptr variable) /* Set the address_taken flag on the indicated variable. */ { if (!variable->address_taken) { a_symbol_ptr var_sym; variable->address_taken = TRUE; var_sym = symbol_for(variable); if (var_sym != NULL && is_context_where_instance_is_needed()) { set_instance_required(var_sym, TRUE, SIR_DEFER_INLINE); } /* if */ /* For a parameter, set param_value_has_been_changed. */ if (variable->is_parameter || variable->is_handler_param) { variable->param_value_has_been_changed = TRUE; } /* if */ #if DO_IL_LOWERING if (il_lowering_underway) { /* If the storage class is "register", change it to "auto", because C doesn't allow taking the address of a register variable (C++ does). */ if (variable->storage_class == (a_storage_class)sc_register) { variable->storage_class = (a_storage_class)sc_auto; } /* if */ } /* if */ #endif /* DO_IL_LOWERING */ } /* if */ } /* set_variable_address_taken */ void set_variable_address_constant(a_variable_ptr variable, a_constant *con, a_boolean set_address_taken_flag) /* Fill in the constant "con" as a ck_address constant for the address of the indicated variable. If set_address_taken_flag is TRUE, the address_taken flag in the variable is set (it's not always set because the constant might be used in a way that doesn't really take the address of the variable, e.g., an lvalue). */ { check_assertion(var_has_static_or_thread_storage_duration(variable) || variable->is_constexpr); clear_constant(con, (a_constant_repr_kind)ck_address); con->variant.address.kind = (an_address_base_kind)abk_variable; con->variant.address.variant.variable = variable; con->type = make_pointer_type(variable->type); if (set_address_taken_flag) { set_variable_address_taken(variable); } /* if */ } /* set_variable_address_constant */ #if DO_IL_LOWERING void set_variable_address_constant_preserving_implicit_cast( a_variable_ptr variable, a_constant *con, a_boolean set_address_taken_flag) /* A variant of set_variable_address_constant that preserves the value of *con's implicit_cast and next fields, as well as any offset. Used in lowering. */ { a_constant_ptr saved_next = con->next; a_boolean has_implicit_cast = con->implicit_cast; a_type_ptr orig_type = con->type; a_targ_ptrdiff_t offset = con->variant.address.offset; set_variable_address_constant(variable, con, set_address_taken_flag); con->next = saved_next; con->variant.address.offset = offset; /* Array cases may require an implicit cast to the decayed pointer type. */ if (has_implicit_cast) implicit_cast(con, orig_type); } /* set_variable_address_constant_preserving_implicit_cast */ #endif /* DO_IL_LOWERING */ void set_constant_address_constant(a_constant_ptr constant, a_constant *con) /* Fill in the constant "con" as a ck_address constant for the address of the indicated constant. */ { clear_constant(con, (a_constant_repr_kind)ck_address); con->variant.address.kind = (an_address_base_kind)abk_constant; con->variant.address.variant.constant = constant; con->type = make_pointer_type(constant->type); check_assertion(constant->kind == (a_constant_repr_kind)ck_string || constant->kind == (a_constant_repr_kind)ck_aggregate || constant->kind == (a_constant_repr_kind)ck_error); } /* set_constant_address_constant */ void set_temporary_address_constant(a_constant_ptr constant, a_constant *con) /* Fill in the constant "con" as a ck_address constant for the address of a unique temporary containing the value of the indicated constant. If the indicated constant is a ck_string (not strictly a "temporary", but handled for convenience), *con is set to a ck_address constant of abk_constant variant; otherwise, the abk_temporary variant is produced instead. */ { clear_constant(con, (a_constant_repr_kind)ck_address); con->variant.address.kind = constant->kind == (a_constant_repr_kind)ck_string ? (an_address_base_kind)abk_constant : (an_address_base_kind)abk_temporary; con->variant.address.variant.constant = constant; con->type = make_pointer_type(constant->type); check_assertion(!is_incomplete_type(constant->type)); } /* set_temporary_address_constant */ a_boolean con_is_exact_addr_of_variable(a_constant_ptr con, a_variable_ptr *var, a_boolean array_decay_allowed) /* If the indicated constant is the exact address of a variable, set *var to the variable and return TRUE. Otherwise, return FALSE. If array_decay_allowed is TRUE, an implicit cast from an array variable to a pointer to its first element is ignored in the determination. */ { a_boolean is_exact_addr = FALSE; while (constant_is(con, ck_template_param)) { /* Eliminate wrappers added to force a constant to be template-dependent. These come up in prototype instantiations. */ if (tpck_is(con, tpck_expression)) { a_constant_ptr base_con; a_boolean explicit_cast; if (is_template_param_cast_constant(con, &base_con, &explicit_cast) && con->type == base_con->type) { con = base_con; } else { break; } /* if */ } else if (tpck_is(con, tpck_dependent_constant)) { a_constant_ptr base_con = con->variant.template_param.variant.constant; if (con->type == base_con->type) { con = base_con; } else { break; } /* if */ } else { break; } /* if */ } /* while */ if (constant_is(con, ck_address) && con->variant.address.kind == (an_address_base_kind)abk_variable && con->variant.address.offset == 0) { a_variable_ptr cvar = con->variant.address.variant.variable; a_type_ptr uctype; /* Check for type compatibility. */ if (!con->implicit_cast || (is_pointer_type(con->type) && (uctype = type_pointed_to(con->type), identical_types(uctype, cvar->type)))) { is_exact_addr = TRUE; } else if (array_decay_allowed && is_pointer_type(con->type) && is_array_type(cvar->type)) { a_type_ptr source_type = cvar->type; a_type_ptr target_type = con->type; source_type = f_skip_typerefs(underlying_array_element_type(source_type)); target_type = f_skip_typerefs(type_pointed_to(con->type)); if (is_array_type(target_type)) { /* If the variable is a multidimensional array, the implicit cast will be to an intermediate array type. */ target_type = f_skip_typerefs(underlying_array_element_type(target_type)); } /* if */ if (identical_types(source_type, target_type)) { is_exact_addr = TRUE; } /* if */ } /* if */ if (is_exact_addr) *var = cvar; } /* if */ return is_exact_addr; } /* con_is_exact_addr_of_variable */ #if GNU_EXTENSIONS_ALLOWED void set_label_address_constant(a_label_ptr label, a_constant *con) /* Fill in the constant "con" as a ck_address constant for the address of the indicated label. */ { clear_constant(con, (a_constant_repr_kind)ck_address); con->variant.address.kind = (an_address_base_kind)abk_label; con->variant.address.variant.label = label; /* Remember that the label has had its address taken. */ label->address_taken = TRUE; /* The address of a label always has type "void *". */ con->type = make_pointer_type(void_type()); } /* set_label_address_constant */ #endif /* GNU_EXTENSIONS_ALLOWED */ void set_ptr_to_member_function_constant(a_routine_ptr routine, a_constant *con) /* Fill in the constant "con" as a pointer-to-member constant for the nonstatic member function indicated by routine. */ { a_type_ptr member_class; a_symbol_ptr member_sym; clear_constant(con, (a_constant_repr_kind)ck_ptr_to_member); con->variant.ptr_to_member.is_function_ptr = TRUE; con->variant.ptr_to_member.variant.routine = routine; /* Note that the class of the pointer is always the class in which the member was defined, not any derived class. See [expr.unary.op] 5.3.1p2. */ /* Get the parent from the symbol rather than the routine in order to get pointers-to-members of anonymous unions right (it doesn't really matter for routines, but just in case). */ member_sym = symbol_for(routine); check_assertion(member_sym != NULL && member_sym->is_class_member); member_class = sym_parent_class(member_sym); if (routine->is_template_function) { instantiate_exception_spec_if_needed(member_sym); } /* if */ con->type = ptr_to_member_type(routine->type, member_class, member_class); } /* set_ptr_to_member_function_constant */ void set_ptr_to_data_member_constant(a_field_ptr field, a_constant *con) /* Fill in the constant "con" as a pointer-to-member constant for the nonstatic data member indicated by field. */ { a_type_ptr member_class; a_symbol_ptr member_sym; clear_constant(con, (a_constant_repr_kind)ck_ptr_to_member); con->variant.ptr_to_member.is_function_ptr = FALSE; con->variant.ptr_to_member.variant.field = field; /* Note that the class of the pointer is always the class in which the member was defined, not any derived class. See [expr.unary.op] 5.3.1p2. */ /* Get the parent from the symbol rather than the field in order to get pointers-to-members of anonymous unions right. */ member_sym = ((a_symbol_ptr)field->source_corresp.assoc_info); check_assertion(member_sym != NULL && member_sym->is_class_member); member_class = sym_parent_class(member_sym); con->type = ptr_to_member_type(field->type, member_class, member_class); } /* set_ptr_to_data_member_constant */ static void clear_source_corresp_for_copy(a_source_correspondence *scp) /* Clear the fields of the source correspondence for an IL entry that was just copied if those fields should not apply to the copy. */ { scp->is_deprecated_or_unavailable = FALSE; #if GENERATE_SOURCE_SEQUENCE_LISTS scp->source_sequence_entry = NULL; #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ #if EXTRA_SOURCE_POSITIONS_IN_IL scp->decl_pos_info = NULL; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ #if ONE_INSTANTIATION_PER_OBJECT scp->per_instantiation_needed_flags = NULL; #endif /* ONE_INSTANTIATION_PER_OBJECT */ scp->name_references = NULL; } /* clear_source_corresp_for_copy */ void copy_constant(const a_constant *from, a_constant *to) /* Copy a constant entry from "from" to "to". */ { *to = *from; /* *from might be a shared constant, an enum constant, etc., so clear the "next" field. */ to->next = NULL; clear_source_corresp_for_copy(&to->source_corresp); /* If the original constant referred indirectly to a backing expression, the copy will not do so since it doesn't have the associated "local-expr-ref" entry. */ to->local_expr_ref = FALSE; #if EXPENSIVE_CHECKING if (constant_is(from, ck_template_param) && tpck_is(from, tpck_expression) && from->variant.template_param.local_expr_ref && expr_node_from_tpck_expression(from) == NULL) { /* Copying a tpck_expression entry that requires a "local-expr-ref" because of memory region constraints results in a missing link since the "local-expr-ref" entry won't be copied along with the constant. */ internal_error("Bad constant copy"); } /* if */ #endif /* EXPENSIVE_CHECKING */ } /* copy_constant */ void copy_template(a_template *from, a_template *to) /* Copy a template entry from "from" to "to". */ { *to = *from; to->next = NULL; clear_source_corresp_for_copy(&to->source_corresp); } /* copy_template */ static a_type_ptr character_type(a_character_kind kind) /* Return a character type entry (tk_integer) corresponding to the given character kind. */ { a_type_ptr result = NULL; switch (kind) { case chk_char: result = integer_type(plain_char_int_kind); break; case chk_wchar_t: result = eff_wchar_t_type(); break; case chk_char8_t: result = eff_char8_t_type(); break; case chk_char16_t: result = eff_char16_t_type(); break; case chk_char32_t: result = eff_char32_t_type(); break; default: unexpected_condition(); } /* switch */ return result; } /* character_type */ void add_constant_to_aggregate(a_constant_ptr con, a_constant_ptr aggr_con, ARG_UNUSED a_base_class_ptr bcp, ARG_UNUSED a_field_ptr fp) /* Add con at the end of the list of constants in the aggregate constant aggr_con. Update some flags in aggr_con if appropriate (e.g., if con uses a designated initializer, then aggr_con does also). If the constant being added to the aggregate is for a class type that may be empty, one of bcp or fp should be set to point to the base class or field that corresponds to the constant. */ { check_assertion(aggr_con->kind == (a_constant_repr_kind)ck_aggregate && (bcp == NULL || fp == NULL)); #if DO_IL_LOWERING if (con->type != NULL && is_class_struct_union_type(con->type)) { if (bcp != NULL && bcp->is_optimized_empty_base) { check_assertion(con->constant_for_base_class); con->initializes_empty_object = TRUE; if (con->kind == (a_constant_repr_kind)ck_aggregate) { con->variant.aggregate.field_or_base.base = bcp; } else { check_assertion(con->kind == (a_constant_repr_kind)ck_dynamic_init); con->variant.dynamic_init.field_or_base.base = bcp; } /* if */ } /* if */ if (fp != NULL && fp->is_optimized_empty_class) { con->initializes_empty_object = TRUE; if (con->kind == (a_constant_repr_kind)ck_aggregate) { con->variant.aggregate.field_or_base.field = fp; } else { check_assertion(con->kind == (a_constant_repr_kind)ck_dynamic_init); con->variant.dynamic_init.field_or_base.field = fp; } /* if */ } /* if */ } /* if */ #endif /* DO_IL_LOWERING */ if (aggr_con->variant.aggregate.first_constant == NULL) { aggr_con->variant.aggregate.first_constant = con; } else { aggr_con->variant.aggregate.last_constant->next = con; } /* if */ aggr_con->variant.aggregate.last_constant = con; if (con->is_partially_initialized) { aggr_con->is_partially_initialized = TRUE; } /* if */ if (con->uses_designated_initializers || con->kind == (a_constant_repr_kind)ck_designator) { aggr_con->uses_designated_initializers = TRUE; } /* if */ /* Define a local macro to avoid code duplication. */ #define is_or_has_dynamic_init_component(con) \ ((con)->kind == (a_constant_repr_kind)ck_dynamic_init || \ ((con)->kind == (a_constant_repr_kind)ck_aggregate && \ (con)->variant.aggregate.has_dynamic_init_component)) if (is_or_has_dynamic_init_component(con) || (con->kind == (a_constant_repr_kind)ck_init_repeat && is_or_has_dynamic_init_component(con->variant.init_repeat.constant))) { aggr_con->variant.aggregate.has_dynamic_init_component = TRUE; } /* if */ #undef is_or_has_dynamic_init_component } /* add_constant_to_aggregate */ a_constant_ptr add_repeat_con(a_constant_ptr elem_con, a_targ_size_t count) /* Return a ck_init_repeat for the given count on top of the given constant. The count can be zero. */ { a_constant_ptr result; result = alloc_constant(ck_init_repeat); result->type = elem_con->type; result->source_corresp.decl_position = elem_con->source_corresp.decl_position; result->variant.init_repeat.count = count; result->variant.init_repeat.constant = elem_con; return result; } /* add_repeat_con */ void explode_string_initializer(a_constant_ptr con) /* If the indicated initializer constant is a string literal constant, explode the string into an aggregate initializer for the individual characters. The constant is updated in place. */ { if (con->kind == (a_constant_repr_kind)ck_string) { a_character_kind char_kind = enum_cast( con->character_kind); unsigned int char_size = (unsigned int)character_size[char_kind]; a_targ_size_t i, len = con->variant.string.length; a_const_char *str = con->variant.string.value; a_constant_ptr char_val = local_constant(); clear_constant(char_val, (a_constant_repr_kind)ck_integer); char_val->type = character_type(char_kind); set_constant_kind(con, (a_constant_repr_kind)ck_aggregate); for (i = 0; i < len; i += char_size) { a_constant_ptr char_con; /* Make a constant for one character of the string. */ if (char_kind == (a_character_kind)chk_char) { set_integer_value(&char_val->variant.integer_value, (a_host_large_integer)str[i]); } else { /* Wide string case. */ unsigned long val = extract_character_from_string(str+i, char_size); set_unsigned_integer_value(&char_val->variant.integer_value, (a_host_large_unsigned)val); } /* if */ char_con = alloc_unshared_constant(char_val); add_constant_to_aggregate(char_con, con, (a_base_class_ptr)NULL, (a_field_ptr)NULL); } /* for */ release_local_constant(&char_val); } /* if */ } /* explode_string_initializer */ a_targ_size_t string_constant_length(a_constant_ptr con) /* Return the number of characters in the given ck_string entry. */ { a_character_kind char_kind = enum_cast( con->character_kind); unsigned int char_size = (unsigned int)character_size[char_kind]; a_targ_size_t len = con->variant.string.length; return len/char_size; } /* string_constant_length */ static an_expr_node_ptr gather_initializer_expressions(a_constant_ptr con) /* Gather any expressions with side effects in the initializer constant con into a single expression, and return a pointer to that expression. If there are no expressions with side effects in con, return NULL. The expressions aren't copied; they're just linked together into one tree. */ { an_expr_node_ptr expr = NULL; if (con->kind == (a_constant_repr_kind)ck_dynamic_init) { a_dynamic_init_ptr dip = con->variant.dynamic_init.ptr; a_boolean suppress_warning; if (dynamic_init_has_side_effects(dip, /*for_unused_var=*/FALSE, &suppress_warning)) { if (dip->kind == (a_dynamic_init_kind)dik_expression) { expr = dip->variant.expression; } else { /* For other cases, e.g., dik_constructor, make an enk_temp_init. */ expr = alloc_expr_node((an_expr_node_kind)enk_temp_init); expr->variant.init.dynamic_init = dip; expr->type = prvalue_type(con->type); } /* if */ } /* if */ } else if (con->kind == (a_constant_repr_kind)ck_aggregate) { /* Visit the members of an aggregate. */ a_constant_ptr mcon; for (mcon = con->variant.aggregate.first_constant; mcon != NULL; mcon = mcon->next) { an_expr_node_ptr mexpr = gather_initializer_expressions(mcon); if (mexpr != NULL) { /* Add the expression to the list using a comma operator. */ if (expr == NULL) { expr = mexpr; } else { expr = make_comma_node(expr, mexpr); } /* if */ } /* if */ } /* for */ } else if (con->kind == (a_constant_repr_kind)ck_init_repeat) { expr = gather_initializer_expressions(con->variant.init_repeat.constant); } /* if */ return expr; } /* gather_initializer_expressions */ an_expr_node_ptr *find_expression_in_initializer(a_constant_ptr con) /* Find an expression in the initializer constant con, and return a pointer to the pointer to it. If no expression is found, change the constant to a ck_dynamic_init with the constant as an expression under it, and return that expression. */ { an_expr_node_ptr *expr_ptr; if (con->kind == (a_constant_repr_kind)ck_dynamic_init) { a_dynamic_init_ptr dip = con->variant.dynamic_init.ptr; if (dip->kind != (a_dynamic_init_kind)dik_expression) { /* Dynamic init other than dik_expression (e.g., dik_constructor). Turn the initialization into a dik_expression with the original initialization as an enk_temp_init under it. */ an_expr_node_ptr expr; expr = alloc_expr_node((an_expr_node_kind)enk_temp_init); expr->variant.init.dynamic_init = dip; expr->type = prvalue_type(con->type); dip = alloc_dynamic_init((a_dynamic_init_kind)dik_expression); dip->variant.expression = expr; con->variant.dynamic_init.ptr = dip; } /* if */ expr_ptr = &dip->variant.expression; } else if (con->kind == (a_constant_repr_kind)ck_aggregate && con->variant.aggregate.first_constant != NULL) { /* Use the first member of a non-empty aggregate. */ a_constant_ptr member_con = con->variant.aggregate.first_constant; check_assertion(member_con != NULL); if (member_con->kind == (a_constant_repr_kind)ck_designator) { /* Advance past a designator for a union. */ member_con = member_con->next; check_assertion(member_con != NULL); } /* if */ while (member_con->kind == (a_constant_repr_kind)ck_init_repeat) { /* We can't add under a ck_init_repeat (the repeated constant must be truly constant), so skip this constant. */ member_con = member_con->next; check_assertion(member_con != NULL); } /* while */ expr_ptr = find_expression_in_initializer(member_con); #if CHECKING } else if (con->kind == (a_constant_repr_kind)ck_init_repeat) { /* ck_init_repeat constants should not appear at this level. */ unexpected_condition(); #endif /* CHECKING */ } else { /* Other constants. Change to a ck_dynamic_init. */ an_expr_node_ptr expr; a_dynamic_init_ptr dip; /* You can't make an expression with array type, so if the constant is a string literal break it into individual characters and change the first one to a ck_dynamic_init. */ if (con->kind == (a_constant_repr_kind)ck_string) { explode_string_initializer(con); con = con->variant.aggregate.first_constant; } /* if */ expr = alloc_node_for_constant(con); dip = alloc_dynamic_init((a_dynamic_init_kind)dik_expression); dip->variant.expression = expr; set_constant_kind(con, (a_constant_repr_kind)ck_dynamic_init); con->variant.dynamic_init.ptr = dip; expr_ptr = &dip->variant.expression; } /* if */ return expr_ptr; } /* find_expression_in_initializer */ void combine_initializers(a_constant_ptr first, a_dynamic_init_ptr first_dip, a_constant_ptr second, a_dynamic_init_ptr second_dip) /* Create the effect of sequentially applying two initializers to the same variable. If an initializer representation is available as "a_constant" variables, they can be passed through first and second. If either of these is NULL, the corresponding first_dip or second_dip should not be null and should point to a dynamic_init representing the initializer for which (presumably) no constant has been created (yet). The entities passed as parameters should be unshared. The entities pointed to by second and second_dip are modified to reflect the combined effect. */ { an_expr_node_ptr first_expr; /* Get the expression for the first constant. */ if (first != NULL) { first_expr = gather_initializer_expressions(first); } else { check_assertion(first_dip != NULL && first_dip->kind == (a_dynamic_init_kind)dik_expression); first_expr = first_dip->variant.expression; if (!node_has_side_effects(first_expr, (a_boolean *)NULL)) { first_expr = NULL; } /* if */ } /* if */ /* If first_expr is NULL here, the first constant contains no expressions with side effects, so no update of the second is required. */ if (first_expr != NULL) { /* Find an expression in the second constant that we can modify. Make one if necessary. */ an_expr_node_ptr *modif_ptr; if (second != NULL) { modif_ptr = find_expression_in_initializer(second); } else { check_assertion(second_dip != NULL && second_dip->kind == (a_dynamic_init_kind)dik_expression); modif_ptr = &second_dip->variant.expression; } /* if */ /* Make a comma node to join the two expressions. */ *modif_ptr = make_comma_node(first_expr, *modif_ptr); } /* if */ } /* combine_initializers */ void combine_initializer_constants(a_constant_ptr first, a_constant_ptr second) /* Modify the constant pointed to by second to represent the effect of sequentially initializing the same variable with *first followed by (the original) *second. In particular, *first can be discarded if it has no side-effects. The constants passed as parameters should be unshared. */ { combine_initializers(first, (a_dynamic_init_ptr)NULL, second, (a_dynamic_init_ptr)NULL); } /* combine_initializer_constants */ a_constant_ptr alloc_unshared_constant_full(a_constant *cp, a_boolean source_in_il, a_boolean suppress_copy) /* Allocate a constant in the current IL memory region, copy the value of *cp into it, and return a pointer to the allocated constant. This routine is used when a constant cannot be shared, as when it is an initializer value. Several fields are cleared or adjusted. Note that the subtree of the constant is not copied unless memory region issues force that; in general, it is assumed that this routine can allocate a new constant entry, do a shallow copy into it, and use any subtree without copying (either it's unshared already, or it can be pointed to by multiple constants). source_in_il is TRUE in cases where the given constant is an allocated IL entry, in which case the il_lowering_flag value of the source constant is copied to the returned constant. When suppress_copy is TRUE, force the shallow copy even if memory region issues seem to suggest a deep copy is needed. */ { a_constant_ptr ucp; if (!suppress_copy && curr_il_region_number == file_scope_region_number && has_non_file_scope_ref(cp)) { ucp = copy_constant_full(cp, (a_constant *)NULL, source_in_il? CE_NO_OPTIONS : CE_SRC_CONSTANT_IS_NOT_ALLOC_IN_IL); } else { ucp = alloc_constant(cp->kind); copy_constant(cp, ucp); if (source_in_il) { /* Copy the value of the il_lowering_flag from one constant to the other (otherwise, when initial_value_for_il_lowering_flag is TRUE, it appears that a copied constant has become lowered and this isn't the case). */ copy_il_lowering_flag(cp, ucp); } /* if */ } /* if */ /* Clear the source correspondence information. This version of the constant isn't the one directly associated with the source entity, if any. */ break_constant_source_corresp(ucp); if (cp->kind == ck_address && cp->variant.address.subobject_path != NULL) { /* The subobject path should not be shared between the constants. */ ucp->variant.address.subobject_path = copy_subobject_path(cp->variant.address.subobject_path); } /* if */ fix_memory_region_problems_in_copied_constant(ucp); return ucp; } /* alloc_unshared_constant_full */ a_constant_ptr alloc_unshared_constant(a_constant *cp) /* Allocate a constant in the current IL memory region, copy the value of *cp into it, and return a pointer to the allocated constant. This routine is used when a constant cannot be shared, as when it is an initializer value. Several fields are cleared or adjusted. Note that the subtree of the constant is not copied unless memory region issues force that; in general, it is assumed that this routine can allocate a new constant entry, do a shallow copy into it, and use any subtree without copying (either it's unshared already, or it can be pointed to by multiple constants). The source constant is assumed to be on the stack; for cases where the source constant is an allocated IL entry, see alloc_unshared_constant_full. */ { return alloc_unshared_constant_full(cp, /*source_in_il=*/FALSE, /*suppress_copy=*/FALSE); } /* alloc_unshared_constant */ a_constant_ptr alloc_unshared_constant_in_region(a_constant *cp, a_boolean in_file_region) /* Allocate an unshared constant in the file scope memory region if in_file_region is TRUE, or in the current IL memory region if in_file_region is FALSE. The value of *cp is copied into the allocated constant. See alloc_unshared_constant for more details. */ { a_constant_ptr result; a_memory_region_number region_to_switch_back_to = NULL_region_number; if (in_file_region && curr_il_region_number != file_scope_region_number) { switch_to_file_scope_region(®ion_to_switch_back_to); } /* if */ result = alloc_unshared_constant(cp); if (region_to_switch_back_to != NULL_region_number) { switch_back_to_original_region(region_to_switch_back_to); } /* if */ return result; } /* alloc_unshared_constant_in_region */ a_subobject_path_ptr copy_subobject_path(a_subobject_path_ptr path) /* Return a copy of the given subobject path. */ { a_subobject_path_ptr copy = NULL, *p_entry = © while (path != NULL) { *p_entry = alloc_subobject_path(); **p_entry = *path; path = path->next; p_entry = &(*p_entry)->next; } /* while */ return copy; } /* copy_subobject_path */ static a_constant_ptr i_copy_constant_full( a_constant_ptr old_constant, a_constant_ptr new_constant, an_expr_copy_options_set options, a_tree_copy_control_block *cblock) /* Make a copy of a constant and its subtree and return a pointer to the copy. options is the set of options for the copy. If new_constant is non-NULL, the copy is placed there (that address must be in allocated IL memory unless the option CE_DEST_CONSTANT_IS_NOT_ALLOC_IN_IL is specified); otherwise, a new constant is allocated. By default, the copy will be an unshared constant, but if the option CE_COPIED_CONSTANTS_MAY_BE_SHARED is specified, the constant may be shared. cblock is a control block for the copy. This is an internal routine (thus the "i_" prefix); copy_constant_full should be called to start a copy. */ { a_constant_ptr old_aggr_con, new_aggr_con; a_boolean may_be_shared = (options & CE_COPIED_CONSTANTS_MAY_BE_SHARED) != 0; a_boolean new_constant_in_il; a_boolean old_constant_in_il = !(options & CE_SRC_CONSTANT_IS_NOT_ALLOC_IN_IL); a_boolean copying_from_one_func_to_another = (options & (CE_DOING_INLINING_OF_FUNCTION_CALL | CE_COPYING_FROM_ONE_FUNC_TO_ANOTHER)) != 0; a_boolean constexpr_master_copy = (options & CE_COPYING_FOR_CONSTEXPR_MASTER_EXPR) != 0; a_boolean force_copy = (constexpr_master_copy || old_constant->part_of_constexpr_master_expr); a_boolean copy_backing_expr = (options & (CE_COPYING_FOR_LOCAL_EXPR_NODE_REF | CE_ALWAYS_COPY_BACKING_EXPRESSIONS)) != 0; a_constant_ptr local_con = local_constant(); an_expr_copy_options_set options_unshared; if (new_constant != NULL) { /* The caller has passed in the address for the copy. Sometimes, the destination and source constant addresses are the same, because only the constant substructure needs copying (to deal with memory region issues). Avoid the shallow copy in such cases because some tools (notably: valgrind) complain. */ if (old_constant != new_constant) { copy_constant(old_constant, new_constant); } /* if */ may_be_shared = FALSE; new_constant_in_il = !(options & CE_DEST_CONSTANT_IS_NOT_ALLOC_IN_IL); } else if (may_be_shared) { /* For the shareable constant case, build up the constant locally and do the allocation at the end of this routine. */ new_constant = local_con; copy_constant(old_constant, new_constant); new_constant_in_il = FALSE; } else { /* Allocate a new unshared constant. */ new_constant = alloc_constant(old_constant->kind); copy_constant(old_constant, new_constant); new_constant_in_il = TRUE; } /* if */ options = (options & ~(an_expr_copy_options_set)(CE_DEST_CONSTANT_IS_NOT_ALLOC_IN_IL | CE_SRC_CONSTANT_IS_NOT_ALLOC_IN_IL)); /* Create a version of the options set with the may-be-shared bit dropped. */ options_unshared = (options & ~(an_expr_copy_options_set) CE_COPIED_CONSTANTS_MAY_BE_SHARED); /* Here, the target constant has been allocated and a shallow copy has been done into it. Copy the subtree as necessary. */ if (constant_is(new_constant, ck_aggregate)) { /* For aggregate constants, copy the subtree also. */ old_aggr_con = old_constant->variant.aggregate.first_constant; new_constant->variant.aggregate.first_constant = NULL; new_constant->variant.aggregate.last_constant = NULL; for (; old_aggr_con != NULL; old_aggr_con = old_aggr_con->next) { new_aggr_con = i_copy_constant_full(old_aggr_con, (a_constant *)NULL, options_unshared, cblock); /* Add the constant to the aggregate list. */ add_constant_to_aggregate(new_aggr_con, new_constant, (a_base_class_ptr)NULL, (a_field_ptr)NULL); } /* for */ } else if (constant_is(new_constant, ck_init_repeat)) { /* For ck_init_repeat constants, copy the subtree also. */ new_constant->variant.init_repeat.constant = i_copy_constant_full(old_constant->variant.init_repeat.constant, (a_constant *)NULL, options_unshared, cblock); } else if (constant_is(new_constant, ck_dynamic_init)) { /* For ck_dynamic_init constants, copy the subtree also. */ new_constant->variant.dynamic_init.ptr = i_copy_dynamic_init(old_constant->variant.dynamic_init.ptr, options_unshared, cblock); } else if (constant_is(new_constant, ck_address)) { if (new_constant->variant.address.kind == (an_address_base_kind)abk_constant || new_constant->variant.address.kind == (an_address_base_kind)abk_temporary) { a_constant_ptr old_constant_pointed_to = old_constant->variant.address.variant.constant; if (force_copy || old_constant_pointed_to->part_of_constexpr_master_expr || (!in_file_scope(old_constant_pointed_to) && (curr_il_region_number == file_scope_region_number || copying_from_one_func_to_another))) { /* For an address constant pointing to a constant, the constant must be copied too if it's in the wrong memory region. This comes up for addresses of strings; without this copy the ck_address could end up in the file scope memory region with the ck_string pointed to in the function scope memory region. */ /* This also comes up in inlining, when we make a copy of a constant from one function scope memory region to another. */ #if DO_IL_LOWERING && ASSIGN_STRING_LITERAL_SEQUENCE_NUMBERS if ((options & CE_REPLACE_STRINGS_BY_VARIABLES) && constant_is(old_constant_pointed_to, ck_string) && old_constant_pointed_to->variant.string.sequence_number != 0) { /* Rewrite a string literal with sequence_number != 0 as a static variable. */ rewrite_address_of_string_as_address_of_variable(new_constant); } else #endif /* DO_IL_LOWERING && ASSIGN_STRING_LITERAL_SEQUENCE_NUMBERS */ /* Do not insert code here. */ { /* See if we've already made a copy of the underlying constant before. */ char *copy = find_copy_remap_address((char *)old_constant_pointed_to, cblock); if (copy != NULL) { /* The underlying constant has already been copied; use that copy. */ new_constant->variant.address.variant.constant = (a_constant*)copy; } else { new_constant->variant.address.variant.constant = i_copy_constant_full(old_constant_pointed_to, (a_constant *)NULL, options, cblock); /* Save this constant off to the side in case it is referred to later in the copy. */ add_copy_remap_entry((char *)old_constant_pointed_to, (char *)new_constant-> variant.address.variant.constant, cblock); } /* if */ } /* if */ } /* if */ } /* if */ if (old_constant->variant.address.subobject_path != NULL) { new_constant->variant.address.subobject_path = copy_subobject_path(old_constant->variant.address.subobject_path); } /* if */ } else if (constant_is(new_constant, ck_template_param)) { switch (new_constant->variant.template_param.kind) { case tpck_param: case tpck_member: case tpck_unknown_function: case tpck_destructor: /* No subtree to copy. */ break; case tpck_expression: { an_expr_node_ptr old_expr, new_expr; #if PROTOTYPE_INSTANTIATIONS_IN_IL new_constant->variant.template_param.local_expr_ref = FALSE; #endif /* PROTOTYPE_INSTANTIATIONS_IN_IL */ old_expr = expr_node_from_tpck_expression(old_constant); if (old_expr != NULL) { /* Make a copy of the expression tree. */ if (!force_copy && !in_file_scope(old_expr) && curr_il_region_number == file_scope_region_number) { /* There's a memory region problem. */ #if PROTOTYPE_INSTANTIATIONS_IN_IL /* In PROTOTYPE_INSTANTIATIONS_IN_IL versions, keep the old expression and fix_memory_region_problems_in_copied_constant will call make_local_expr_node_ref. */ new_expr = old_expr; #else /* !PROTOTYPE_INSTANTIATIONS_IN_IL */ new_expr = NULL; #endif /* PROTOTYPE_INSTANTIATIONS_IN_IL */ } else { /* Make a copy of the expression tree. */ new_expr = i_copy_expr_tree(old_expr, options, cblock); } /* if */ new_constant->variant.template_param.variant.expr = new_expr; } /* if */ } break; case tpck_address: case tpck_dependent_constant: new_constant->variant.template_param.variant.constant = i_copy_constant_full( old_constant->variant.template_param.variant.constant, (a_constant *)NULL, options, cblock); break; case tpck_concat_string_literals: { a_constant_ptr old_list = old_constant->variant.template_param .variant.string_literal_list, new_list = NULL, last_con = NULL, new_con; for (; old_list != NULL; old_list = old_list->next) { new_con = i_copy_constant_full(old_list, (a_constant*)NULL, options, cblock); if (last_con == NULL) { new_list = new_con; } else { last_con->next = new_con; } /* if */ last_con = new_con; } /* for */ new_constant->variant.template_param.variant.string_literal_list = new_list; } break; case tpck_sizeof: case tpck_datasizeof: case tpck_alignof: case tpck_uuidof: case tpck_typeid: case tpck_noexcept: { an_expr_node_ptr old_expr, new_expr; #if PROTOTYPE_INSTANTIATIONS_IN_IL new_constant->variant.template_param.local_expr_ref = FALSE; #endif /* PROTOTYPE_INSTANTIATIONS_IN_IL */ old_expr = old_constant->variant.template_param.variant. templ_sizeof.expr; if (old_expr != NULL) { /* Make a copy of the expression tree. */ if (!force_copy && !in_file_scope(old_expr) && curr_il_region_number == file_scope_region_number) { /* There's a memory region problem, so drop the expression (we still have the type). */ #if PROTOTYPE_INSTANTIATIONS_IN_IL /* In PROTOTYPE_INSTANTIATIONS_IN_IL versions, keep the old expression and fix_memory_region_problems_in_copied_constant will call make_local_expr_node_ref. */ new_expr = old_expr; #else /* !PROTOTYPE_INSTANTIATIONS_IN_IL */ new_expr = NULL; #endif /* PROTOTYPE_INSTANTIATIONS_IN_IL */ } else { /* Make a copy of the expression tree. */ new_expr = i_copy_expr_tree(old_expr, options, cblock); } /* if */ new_constant->variant.template_param.variant.templ_sizeof.expr = new_expr; } /* if */ } break; case tpck_template_ref: new_constant->variant.template_param.variant.template_ref.con = i_copy_constant_full( old_constant->variant.template_param.variant.template_ref.con, (a_constant *)NULL, options, cblock); new_constant->variant.template_param.variant.template_ref.arg_list = copy_template_arg_list(old_constant->variant.template_param. variant.template_ref.arg_list); break; case tpck_integer_pack: new_constant->variant.template_param.variant.bound = i_copy_constant_full( old_constant->variant.template_param.variant.bound, (a_constant_ptr)NULL, options, cblock); break; default: unexpected_condition_str("i_copy_constant_full: bad templ param kind"); } /* if */ } else if (constant_is(new_constant, ck_reflection)) { a_reflection_value *rvp = &new_constant->variant.reflection; #if DO_IL_LOWERING if (il_lowering_underway) { /* Reflection values in lowering are meaningless. */ rvp->entity.ptr = NULL; rvp->entity.kind = iek_none; } else #endif /* DO_IL_LOWERING */ /* Do not insert code here. */ if (!in_file_scope(rvp->entity.ptr) && (curr_il_region_number == file_scope_region_number || copying_from_one_func_to_another)) { switch (rvp->entity.kind) { case iek_expr_node: rvp->entity.ptr = (char*)i_copy_expr_tree( (an_expr_node*)rvp->entity.ptr, options, cblock); break; case iek_constant: rvp->entity.ptr = (char*)i_copy_constant_full( (a_constant*)rvp->entity.ptr, (a_constant *)NULL, options, cblock); break; default: unexpected_condition(); } /* switch */ } /* if */ } /* if */ if (old_constant->expr == NULL) { /* Skip some processing of backing expressions if there isn't one. */ } else if (copying_from_one_func_to_another || force_copy) { /* When copying for inlining, the expression pointed to is in a function scope memory region and can't be used in the new function scope memory region. The expression might be copyable in some cases, but there's no point in trying too hard since the copy does not correspond to anything in the source. */ /* Also when copying into or out of the constexpr master copy. */ new_constant->expr = NULL; } else if (old_constant_in_il && new_constant_in_il && in_file_scope(old_constant) && !in_file_scope(new_constant)) { /* If we're copying the constant from the file scope memory region to the function scope memory region, remove the backing expression to prevent memory region issues. */ new_constant->expr = NULL; } /* if */ /* The case where new_constant is in the file scope memory region while old_constant->expr lives in a function scope memory region is handled by fix_memory_region_problems_in_copied_constant below. */ if (constexpr_master_copy) { /* Constants created in making the master copy of a constexpr evaluation expression are marked as such. */ new_constant->part_of_constexpr_master_expr = TRUE; } /* if */ if (copy_backing_expr && new_constant->expr != NULL) { /* Make a copy of the backing expression as well. */ new_constant->expr = i_copy_expr_tree(new_constant->expr, options, cblock); } /* if */ if (may_be_shared) { new_constant = alloc_shareable_constant(new_constant); } else if (new_constant_in_il) { fix_memory_region_problems_in_copied_constant(new_constant); } /* if */ if (old_constant_in_il && (new_constant_in_il || may_be_shared)) { /* Copy the value of the il_lowering_flag from one constant to the other (otherwise, when initial_value_for_il_lowering_flag is TRUE, it appears that a copied constant has become lowered and this isn't the case). */ copy_il_lowering_flag(old_constant, new_constant); } /* if */ release_local_constant(&local_con); return new_constant; /*lint !e809*/ } /* i_copy_constant_full */ a_constant_ptr copy_constant_full(a_constant_ptr old_constant, a_constant_ptr new_constant, an_expr_copy_options_set options) /* Make a copy of a constant and its subtree and return a pointer to the copy. options is the set of options for the copy. If new_constant is non-NULL, the copy is placed there (that address must be in allocated IL memory unless the option CE_DEST_CONSTANT_IS_NOT_ALLOC_IN_IL is specified); otherwise, a new constant is allocated. By default, the copy will be an unshared constant, but if the option CE_COPIED_CONSTANTS_MAY_BE_SHARED is specified, the constant may be shared. See copy_unshared_constant for a simple interface to this routine for the usual case. */ { a_tree_copy_control_block cblock; /* This is a wrapper around i_copy_constant_full that initializes the control block to be used for the entire copy. */ clear_tree_copy_control_block(&cblock); new_constant = i_copy_constant_full(old_constant, new_constant, options, &cblock); done_with_tree_copy_control_block(&cblock); return new_constant; } /* copy_constant_full */ a_constant_ptr copy_unshared_constant(a_constant_ptr old_constant) /* Simple interface to copy_constant_full for the usual case. The source constant is assumed to be an allocated IL constant. */ { return copy_constant_full(old_constant, (a_constant *)NULL, CE_NO_OPTIONS); } /* copy_unshared_constant */ static a_hash_value hash_string(a_const_char *ptr) /* Return a hash value developed from the string pointed to by ptr. */ { a_hash_value hash_value = 0; a_const_char *cptr; for (cptr = ptr; *cptr != '\0'; cptr++) { hash_value = (hash_value << 5) + hash_value + (a_hash_value)(*cptr); } /* for */ return hash_value; } /* hash_string */ /* Forward declarations. */ static a_hash_value hash_type(a_type_ptr type); static a_hash_value hash_class_type(a_type_ptr type); static a_hash_value hash_routine(a_routine_ptr rp); static a_hash_value hash_name(a_source_correspondence_ptr scp) /* Return a hash value developed from the name (including enclosing scopes) in the indicated source correspondence entry. */ { a_hash_value hash_value = 0; a_const_char *name = unmangled_name_of(scp); if (name != NULL) { hash_value = hash_string(name); } /* if */ if (scp->enclosing_routine != NULL) { hash_value += hash_routine(scp->enclosing_routine); } else if (scp->parent_scope != NULL) { a_scope_ptr scope = scp->parent_scope; switch (scope->kind) { case sck_class_struct_union: hash_value += hash_class_type(scope->variant.assoc_type); break; case sck_namespace: { a_namespace_ptr nsp = scope->variant.assoc_namespace; if (nsp->hash_value == 0) { nsp->hash_value = hash_name(&nsp->source_corresp); if (nsp->hash_value == 0) nsp->hash_value++; } /* if */ hash_value += nsp->hash_value; } break; case sck_enum: hash_value += hash_name(&scope->variant.assoc_type->source_corresp); break; default: /* Nothing to be done. */ break; } /* switch */ } /* if */ return hash_value; } /* hash_name */ static a_hash_value hash_template(a_template_ptr templ) /* Return a hash value for the indicated template. */ { a_hash_value hash_value; templ = skip_simple_alias_templates(templ); hash_value = hash_name(&templ->source_corresp); return hash_value; } /* hash_template */ a_hash_value hash_template_arg_list(a_template_arg_ptr tap) /* Return a hash value for the indicated template argument list. */ { a_hash_value hash_value = 0; uint32_t pos = 1; uint32_t next_pos; for (; tap != NULL; tap = tap->next, pos = next_pos) { next_pos = pos + 1; switch (tap->kind) { case tak_type: if (tap->variant.type != NULL) { hash_value += hash_type(tap->variant.type) * next_pos; } /* if */ break; case tak_nontype: /* The argument position is factored in so that <1,2,3> hashes differently than <3,2,1>. */ if (tap->is_array_bound_of_unknown_type) { hash_value += (1 + (a_hash_value)tap->variant.integer_value) << ((pos * 3) % 32); } else { a_constant_ptr cp = tap->variant.constant; if (cp != NULL) { hash_value += (1 + hash_constant(cp)) << ((pos * 3) % 32); } /* if */ } /* if */ break; case tak_template: if (tap->variant.templ.ptr != NULL) { hash_value += hash_template(tap->variant.templ.ptr) * next_pos; } /* if */ break; case tak_start_of_pack_expansion: /* Ignore placeholders for position counting. */ next_pos = pos; break; default: unexpected_condition(); break; } /* switch */ } /* for */ return hash_value; } /* hash_template_arg_list */ static a_hash_value hash_routine(a_routine_ptr rp) /* Return a hash value for the indicated routine. Store the hash value in the routine so that it does not have to be recomputed again. */ { a_hash_value hash_value; if (rp->hash_value != 0) { hash_value = rp->hash_value; goto done; } /* if */ hash_value = hash_name(&rp->source_corresp); /* Include the template arguments if there are any. */ if (rp->template_arg_list != NULL) { hash_value += hash_template_arg_list(rp->template_arg_list); } /* if */ /* A zero value is used to indicate that the hash has not been computed yet, so make sure the value is not zero. */ if (hash_value == 0) hash_value++; /* Save the computed hash value. */ rp->hash_value = hash_value; done: return hash_value; } /* hash_routine */ static a_hash_value hash_variable(a_variable_ptr vp) /* Return a hash value for the indicated variable. */ { a_hash_value hash_value; hash_value = hash_name(&vp->source_corresp); /* Include the template arguments if there are any. */ if (vp->template_info != NULL) { hash_value += hash_template_arg_list(vp->template_info->template_arg_list); } /* if */ return hash_value; } /* hash_variable */ static a_hash_value hash_class_type(a_type_ptr type) /* Return a hash value for the indicated class type. Store the hash value in the class type supplement so that it does not have to be recomputed again. */ { a_class_type_supplement_ptr ctsp; a_hash_value hash_value; ctsp = type->variant.class_struct_union.extra_info; if (ctsp->hash_value != 0) { hash_value = ctsp->hash_value; goto done; } /* if */ hash_value = hash_name(&type->source_corresp); /* Include the template arguments if there are any. */ if (ctsp->template_arg_list != NULL) { hash_value += hash_template_arg_list(ctsp->template_arg_list); } /* if */ #if NEED_NAME_MANGLING if (symbol_for(type) != NULL) { a_class_symbol_supplement_ptr cssp = class_symbol_supp(symbol_for(type)); hash_value += (a_hash_value)cssp->discriminator; } /* if */ #endif /* NEED_NAME_MANGLING */ /* A zero value is used to indicate that the hash has not been computed yet, so make sure the value is not zero. */ if (hash_value == 0) hash_value++; /* Save the computed hash value. */ ctsp->hash_value = hash_value; done: return hash_value; } /* hash_class_type */ #if MICROSOFT_EXTENSIONS_ALLOWED || GNU_X86_ATTRIBUTES_ALLOWED a_boolean routine_has_default_calling_convention( a_routine_type_supplement_ptr rtsp) /* Given a routine's type supplement ptr, return TRUE if the associated routine has a calling convention equivalent to the default calling convention; otherwise, return FALSE. */ { a_boolean result; a_calling_convention cc = rtsp->calling_convention; if (cc == cc_default) { result = TRUE; goto done; } /* if */ if (ms_extensions && !target_is_32_bit_x86_based()) { /* Microsoft x86-64 conventions only distinguish __vectorcall and __clrcall from other conventions. All other conventions (__cdecl, __fastcall, etc.) are accepted but have no effect. */ if (cc != cc_vectorcall && cc != cc_clrcall) { result = TRUE; goto done; } /* if */ } /* if */ if (ms_extensions && rtsp->this_class != NULL) { /* The default calling convention for member functions is cc_thiscall. */ result = (cc == cc_thiscall); } else { result = (cc == default_calling_convention); } /* if */ done: return result; } /* routine_has_default_calling_convention */ a_calling_convention normalized_calling_conv(a_calling_convention cc) /* Return the canonical calling convention corresponding to cc in the current mode. In particular, Microsoft treats the old __fastcall, __stdcall, and __cdecl conventions identically to the default calling convention when targeting 64-bit platforms: For those calling conventions this function returns cc_default if the target is a 64-bit x86 platform. */ { a_calling_convention result_cc = cc; if (ms_extensions && !target_is_32_bit_x86_based()) { /* MSVC's 64-bit ABI treats __fastcall, __stdcall, and __cdecl identically to the default calling convention. */ if (cc == cc_fastcall || cc == cc_stdcall || cc == cc_cdecl) { result_cc = cc_default; } /* if */ } /* if */ return result_cc; } /* normalized_calling_conv */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED || GNU_X86_ATTRIBUTES_ALLOWED */ /* Variable set by the following traverse_expr processing routine. */ STATIC_THREAD a_hash_value expr_hash_value = 0; static void hash_expr_node(an_expr_node_ptr expr, an_expr_or_stmt_traversal_block_ptr tblock) /* This routine is called by traverse_expr in a top-down traversal of an expression tree. It updates the hash value in expr_hash_value with the hash value for the indicated expression node. */ { expr_hash_value = 31*expr_hash_value + (a_hash_value)expr->kind; /* Note that any sub-expressions, types, and constants are already covered by the generic expression traversal. */ switch (expr->kind) { case enk_routine: expr_hash_value += hash_routine(expr->variant.routine.ptr); break; case enk_variable: expr_hash_value += hash_variable(expr->variant.variable.ptr); break; case enk_field: expr_hash_value += hash_name(&expr->variant.field.ptr->source_corresp); break; case enk_new_delete: { a_new_delete_supplement_ptr ndsp = expr->variant.new_delete; expr_hash_value += 2*(a_hash_value)ndsp->is_new + 4*(a_hash_value)ndsp->placement_new + 8*(a_hash_value)ndsp->array_delete + 16*(a_hash_value)ndsp->global_new_or_delete + 32*(a_hash_value)ndsp->has_new_initializer + 64*(a_hash_value)ndsp ->new_initializer_is_brace_enclosed + 128*(a_hash_value)ndsp ->new_initializer_is_paren_aggr_init + 256*(a_hash_value)ndsp->deducible_type + 512*(a_hash_value)ndsp->parenthesized_type_id; if (ndsp->routine != NULL) { expr_hash_value += hash_routine(ndsp->routine); } /* if */ } break; case enk_typeid: expr_hash_value += 2*(a_hash_value)expr->variant.typeid_info.is_dynamic; break; case enk_alignof: expr_hash_value += 2*(a_hash_value)expr->variant.sizeof_info .is_std_alignof; break; case enk_param_ref: expr_hash_value += 7*expr->variant.param_ref.param_num + expr->variant.param_ref.levels_up; break; case enk_fold: expr_hash_value += 15*(a_hash_value)expr->variant.fold.operator_token + 7*(a_hash_value)expr->variant.fold.left_associative; break; case enk_concept_id: expr_hash_value += hash_template( expr->variant.concept_id.concept_template); break; case enk_template_name: expr_hash_value += hash_template(expr->variant.template_name); break; case enk_sizeof_pack: if (expr->variant.sizeof_pack.is_template_template) { expr_hash_value += hash_template( expr->variant.sizeof_pack.variant.templ); } /* if */ break; case enk_operation: expr_hash_value += 7*(a_hash_value)expr->variant.operation.kind; break; case enk_builtin_operation: expr_hash_value += 7*(a_hash_value)expr->variant.builtin_operation.kind; break; default: /* Nothing to be done. */ break; } /* switch */ } /* hash_expr_node */ static void hash_expr_type(a_type_ptr type, an_expr_or_stmt_traversal_block_ptr tblock) /* This routine is called by traverse_expr in a top-down traversal of an expression tree. It updates the hash value in expr_hash_value with the hash value for the indicated type. */ { expr_hash_value += hash_type(type); } /* hash_expr_type */ static void hash_expr_constant(a_constant_ptr constant, an_expr_or_stmt_traversal_block_ptr tblock) /* This routine is called by traverse_expr in a top-down traversal of an expression tree. It updates the hash value in expr_hash_value with the hash value for the indicated constant. */ { expr_hash_value += hash_constant(constant); /* Suppress the subtree walk as hash_constant already covers the whole subtree. */ tblock->suppress_subtree_walk = TRUE; } /* hash_expr_constant */ a_hash_value hash_expr(an_expr_node_ptr expr) /* Return a hash value for the indicated expression. This is used in cases where a dependent type operator (like a dependent decltype) resolves to an unknown template parameter type. */ { Value_saver saved_expr_hash_value(&expr_hash_value, 0); an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = hash_expr_node; tblock.process_type = hash_expr_type; tblock.process_constant = hash_expr_constant; tblock.process_non_dynamic_constants = TRUE; traverse_expr(expr, &tblock); return expr_hash_value; } /* hash_expr */ static a_hash_value hash_type(a_type_ptr type) /* Return a hash value for the indicated type. This is used in some cases to refine the hash value developed in hash_constant. */ { a_hash_value hash_value = 0; /* Only pointers to class types are particularly important here. */ /* Note that the address of the type or its subtypes should not be used in determining the hash value (see comment in hash_constant). */ /* Skip typerefs, but update the hash value with any dependent type operator expressions. */ while (type->kind == tk_typeref) { if (type->variant.typeref.is_dependent_type_operator) { a_typeref_type_supplement_ptr ttsp = type->variant.typeref.extra_info; if (ttsp->expr != NULL) { hash_value += hash_expr(ttsp->expr); } /* if */ if (is_typeref_kind(type, trk_pack_index)) { /* Also hash the pack type operand of a pack-index-specifier. */ hash_value += 17*hash_type(ttsp->operator_type_arg); } /* if */ } /* if */ type = type->variant.typeref.type; } /* while */ switch (type->kind) { case tk_integer: hash_value += (a_hash_value)((type->variant.integer.int_kind * 13) + 53); if (is_bit_precise_kind(type->variant.integer.int_kind)) { hash_value += (a_hash_value)(integer_type_supp(type)->bit_width * 31 + 7); } /* if */ if (type->variant.integer.enum_type) { hash_value += (a_hash_value)( (type->variant.integer.is_scoped_enum * 4) + 2); hash_value += hash_name(&type->source_corresp); } /* if */ break; #if FIXED_POINT_ALLOWED case tk_fixed_point: hash_value += type->variant.fixed_point.saturating; hash_value += type->variant.fixed_point.is_fract_type * 2; hash_value += type->variant.fixed_point.is_unsigned * 4; hash_value += type->variant.fixed_point.precision * 8; hash_value += 131; break; #endif /* FIXED_POINT_ALLOWED */ #if C99_IL_EXTENSIONS_SUPPORTED case tk_complex: case tk_imaginary: #endif /* C99_IL_EXTENSIONS_SUPPORTED */ case tk_float: hash_value += type->variant.float_kind + 87; break; case tk_pointer: hash_value += hash_type(type->variant.pointer.type) + 107 + type->variant.pointer.is_reference + type->variant.pointer.is_rvalue_reference*2 #if MICROSOFT_EXTENSIONS_ALLOWED + type->variant.pointer.is_handle*4 + type->variant.pointer.is_interior_ptr*7 + type->variant.pointer.is_pin_ptr*13 #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ ; break; case tk_array: hash_value += hash_type(type->variant.array.element_type) + 307; if (!has_unknown_specified_bound(type)) { hash_value += (a_hash_value) (type->variant.array.variant.number_of_elements); } /* if */ break; case tk_struct: case tk_class: case tk_union: hash_value += hash_class_type(type); break; case tk_routine: { a_routine_type_supplement_ptr rtsp; a_param_type_ptr ptp; uint32_t pos; rtsp = type->variant.routine.extra_info; if (type->variant.routine.return_type != NULL) { hash_value += hash_type(type->variant.routine.return_type); } /* if */ for (ptp = rtsp->param_type_list, pos = 0; ptp != NULL; ptp = ptp->next, pos++) { hash_value += hash_type(ptp->type); /* The argument position is factored in so that (x,y,z) hashes differently than (z,y,x). */ hash_value = hash_value + (hash_value * (pos+1)); } /* for */ if (rtsp->this_class != NULL) { hash_value += hash_type(rtsp->this_class); } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (ms_extensions && !routine_has_default_calling_convention(rtsp)) { /* Add a calling convention to the hash for every calling convention except calling conventions equivalent to the default calling convention. This ensures all spellings of the default calling convention hash equivalently while other calling conventions have a modified hash. */ hash_value += rtsp->calling_convention; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } break; case tk_typeref: unexpected_condition(); break; #if GNU_VECTOR_TYPES_ALLOWED case tk_vector: hash_value += hash_type(type->variant.vector.element_type) + 331; if (type->variant.vector.size_constant != NULL) { hash_value += 5*hash_constant(type->variant.vector.size_constant); } /* if */ break; case tk_scalable_vector: hash_value += hash_type(type->variant.scalable_vector.element_type); hash_value += 17*type->variant.scalable_vector.tuple_elements; break; case tk_riscv_vector: hash_value += hash_type(type->variant.riscv_vector.element_type); if (type->variant.riscv_vector.length_multiplier >= 0) { hash_value += 17*(a_hash_value)type->variant.riscv_vector .length_multiplier; } else { hash_value += 31*(a_hash_value)(-type->variant.riscv_vector .length_multiplier); } /* if */ hash_value += 83*type->variant.riscv_vector.tuple_elements; break; #endif /* GNU_VECTOR_TYPES_ALLOWED */ case tk_template_param: { a_template_param_type_supplement_ptr tpts; tpts = type->variant.template_param.extra_info; hash_value += 499 + (unsigned)type->variant.template_param.kind + type->variant.template_param.is_pack; /* If the template parameter has a parent scope, include that in the hash value. */ if (type->source_corresp.parent_scope != NULL) { hash_value += (a_hash_value)type->source_corresp.parent_scope->number; } /* if */ hash_value += type->source_corresp.decl_position.seq + type->source_corresp.decl_position.column; if (type->variant.template_param.kind == (a_template_param_type_kind)tptk_param) { hash_value += ((a_hash_value)tpts->coordinates.depth << 8) + tpts->coordinates.position; } /* if */ } break; case tk_ptr_to_member: hash_value += hash_type(type->variant.ptr_to_member.type) + hash_type(type->variant.ptr_to_member.class_of_which_a_member); break; default: hash_value += (a_hash_value)type->kind; } /* switch */ return hash_value; } /* hash_type */ a_hash_value hash_constant(a_constant *cp) /* Return the hash value for the indicated constant. */ { a_hash_value hash_value = 0; a_targ_size_t length; a_boolean ovflo; a_const_char *p; /* Compute a hash value from the constant. The hash doesn't have to be perfect, but it should spread the expected constants fairly widely. The hash should *not* involve the address of the constant or its type; such an approach was tried, and works well from a hashing point of view, but has the undesirable property that it makes the order of the shared constants in the IL highly dependent on the host machine, which means the same version of the front end compiled on two different hosts will generate different IL and perhaps different object code on those two machines. */ switch (cp->kind) { #if UPC_EXTENSIONS_ALLOWED case ck_upc_mythread: hash_value = 237; break; case ck_upc_threads: #endif /* UPC_EXTENSIONS_ALLOWED */ case ck_integer: /* Integer. Use the constant itself as the hash value. */ hash_value = (a_hash_value)value_of_integer_constant(cp, &ovflo); break; #if FIXED_POINT_ALLOWED case ck_fixed_point: /* Fixed-point constant: Use a host-dependent hash-function. */ hash_value = fxp_hash(&cp->variant.fixed_point_value); break; #endif /* FIXED_POINT_ALLOWED */ case ck_string: /* String. Hash all the characters. */ hash_value = 100; #if DO_IL_LOWERING && ASSIGN_STRING_LITERAL_SEQUENCE_NUMBERS hash_value += (a_hash_value)cp->variant.string.sequence_number; #endif /* DO_IL_LOWERING && ASSIGN_STRING_LITERAL_SEQUENCE_NUMBERS */ for (length = cp->variant.string.length, p = cp->variant.string.value; length > 0; length--, p++) { hash_value = (hash_value << 5) + hash_value + (a_hash_value)(*p); } /* for */ break; case ck_float: #if C99_IL_EXTENSIONS_SUPPORTED case ck_imaginary: #endif /* C99_IL_EXTENSIONS_SUPPORTED */ /* Use a host-dependent routine for floating-point constants. */ hash_value = 500 + fp_hash(&cp->variant.float_value); break; #if C99_IL_EXTENSIONS_SUPPORTED case ck_complex: hash_value = 250 + fp_hash(&cp->variant.complex_value->real) + fp_hash(&cp->variant.complex_value->imag); break; #endif /* C99_IL_EXTENSIONS_SUPPORTED */ case ck_address: /* Address constant. If the thing pointed to is named, hash the name; otherwise (for the address of a constant), hash the constant pointed to. */ switch (cp->variant.address.kind) { case abk_routine: hash_value = hash_routine(cp->variant.address.variant.routine); break; case abk_variable: hash_value = hash_variable(cp->variant.address.variant.variable); break; case abk_constant: /* Hash the name if the constant has a name; otherwise, hash the constant pointed to. */ if (has_name(cp->variant.address.variant.constant)) { hash_value = hash_name(&cp->variant.address.variant.constant->source_corresp); } else { hash_value = hash_constant(cp->variant.address.variant.constant); } /* if */ break; case abk_temporary: hash_value = hash_constant(cp->variant.address.variant.constant); break; #if MICROSOFT_EXTENSIONS_ALLOWED case abk_uuidof: /* Note that the type is not included in the hash because two constants with the same UUID value but different types are considered equal (e.g., by compare_constants). */ { a_const_char *uuid_string; hash_value = 231; uuid_string = uuid_string_of_type(cp->type); if (uuid_string != NULL) hash_value += hash_string(uuid_string); } break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case abk_typeid: #if MICROSOFT_EXTENSIONS_ALLOWED case abk_cli_typeid: #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ hash_value = 233; if (cp->variant.address.variant.type != NULL) { hash_value += hash_type(cp->variant.address.variant.type); } /* if */ break; #if MICROSOFT_EXTENSIONS_ALLOWED case abk_cli_array: hash_value = 235; hash_value += cp->source_corresp.decl_position.seq + cp->source_corresp.decl_position.column; break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case abk_label: /* Hash the name of the label. */ check_assertion(has_name(cp->variant.address.variant.label)); hash_value = hash_name(&cp->variant.address.variant.label->source_corresp); break; case abk_param_ref: hash_value = 251 * cp->variant.address.variant.param_ref.param_num; break; default: unexpected_condition_str("hash_constant: bad address constant kind"); } /* switch */ /* Add the offset in the address constant into the hash value. */ hash_value += (a_hash_value)(cp->variant.address.offset + 1000); break; case ck_ptr_to_member: /* Hash the name of the member in a pointer-to-member constant. */ hash_value = 0; if (cp->variant.ptr_to_member.is_function_ptr) { a_routine_ptr rp = cp->variant.ptr_to_member.variant.routine; if (rp != NULL) hash_value = hash_routine(rp); } else { a_field_ptr fp = cp->variant.ptr_to_member.variant.field; if (fp != NULL) hash_value = hash_name(&fp->source_corresp); } /* if */ hash_value += 250; break; case ck_template_param: hash_value = 499; switch (cp->variant.template_param.kind) { case tpck_expression: { an_expr_node_ptr expr = cp->variant.template_param.local_expr_ref ? find_local_expr_node((char *)cp, lerk_tpl_param_expr) : cp->variant.template_param.variant.expr; hash_value += hash_expr(expr); } break; case tpck_param: /* For simple non-type template parameters, use an integer representation of the pointer. In general, this means that those entries won't be found, but this is okay because, in general, there can be multiple versions of nonreal types. */ hash_value += (a_hash_value)possible_lossy_cast_from_pointer(cp); break; case tpck_template_ref: hash_value += hash_constant(cp->variant.template_param .variant.template_ref.con) + hash_template_arg_list(cp->variant.template_param .variant.template_ref.arg_list); break; case tpck_member: hash_value += hash_name(&cp->source_corresp); break; case tpck_unknown_function: if (cp->variant.template_param.variant.unknown_function .conversion_type != NULL) { hash_value += hash_type(cp->variant.template_param .variant.unknown_function.conversion_type); } /* if */ hash_value += (a_hash_value)cp->variant.template_param .variant.unknown_function.opname_kind; break; case tpck_address: hash_value += hash_constant( cp->variant.template_param.variant.constant); break; case tpck_dependent_constant: hash_value += 17 + hash_constant( cp->variant.template_param.variant.constant); break; case tpck_concat_string_literals: { a_constant_ptr elem_cp = cp->variant.template_param .variant.string_literal_list; hash_value += 23; for (; elem_cp != NULL; elem_cp = elem_cp->next) { hash_value = 37*hash_value + hash_constant(elem_cp); } /* for */ } break; case tpck_sizeof: case tpck_datasizeof: case tpck_alignof: case tpck_uuidof: case tpck_typeid: case tpck_noexcept: if (cp->variant.template_param.variant.templ_sizeof.type != NULL) { hash_value += hash_type( cp->variant.template_param.variant.templ_sizeof.type); } /* if */ if (cp->variant.template_param.variant.templ_sizeof.expr != NULL) { hash_value += hash_expr( cp->variant.template_param.variant.templ_sizeof.expr); } /* if */ break; case tpck_destructor: hash_value += hash_type( cp->variant.template_param.variant.destructor.type) + 2*(a_hash_value)cp->variant.template_param.variant .destructor.unqualified; break; case tpck_integer_pack: hash_value += hash_constant( cp->variant.template_param.variant.bound); break; default: unexpected_condition_str("hash_constant: bad template param kind"); } /* switch */ break; #if GNU_EXTENSIONS_ALLOWED case ck_label_difference: /* Use the hash values of the constants pointed to. */ hash_value = hash_constant(cp->variant.label_difference.from_address) + 3*hash_constant(cp->variant.label_difference.to_address) + 511; break; #endif /* GNU_EXTENSIONS_ALLOWED */ #if DO_IL_LOWERING && GENERATE_EH_TABLES && !DO_FULL_PORTABLE_EH_LOWERING case ck_stack_offset: hash_value = hash_variable(cp->variant.stack_offset.variable) + cp->variant.stack_offset.offset + 350; break; #endif /* DO_IL_LOWERING && ... */ case ck_reflection: { a_tagged_pointer *entity = &cp->variant.reflection.entity; hash_value = 17*31 + (a_hash_value)entity->kind; switch (entity->kind) { case iek_constant: hash_value = hash_value*31 + hash_constant((a_constant*)entity->ptr); break; case iek_type: { a_type *tp = (a_type*)entity->ptr; if (type_is_typedef(tp)) { hash_value = hash_value*31 + hash_name(&cp->source_corresp); } /* if */ hash_value = hash_value*31 + hash_type((a_type*)entity->ptr); } break; default: hash_value = hash_value*31 + (a_hash_value)cast_from_pointer(entity->ptr); break; } /* switch */ } break; default: hash_value = (a_hash_value)(200 + cp->kind); break; } /* switch */ if (constant_is(cp, ck_integer) || constant_is(cp, ck_address) || constant_is(cp, ck_ptr_to_member) || constant_is(cp, ck_template_param)) { /* Work the type into the hash. This is important when you have lots of NULL pointer constants for a lot of different types. */ hash_value += hash_type(cp->type); } /* if */ #if DEBUG if (debug_level >= 5) { fprintf(f_debug, "hash_constant, hash_value = %u\n", (unsigned int)hash_value); } /* if */ #endif /* DEBUG */ return hash_value; } /* hash_constant */ static a_boolean compare_expression_lists( an_expr_node_ptr list1, an_expr_node_ptr list2, a_compare_constants_options_set options) /* Return TRUE if list1 and list2 are equivalent expression lists. options is a set of flags that control the way in which certain comparisons are done. */ { an_expr_node_ptr expr1 = list1, expr2 = list2; a_boolean eq; for (;; expr1 = expr1->next, expr2 = expr2->next) { if (expr1 == NULL && expr2 == NULL) { eq = TRUE; break; } else if (expr1 == NULL || expr2 == NULL) { eq = FALSE; break; } else if (!compare_expressions(expr1, expr2, options)) { eq = FALSE; break; } /* if */ } /* for */ return eq; } /* compare_expression_lists */ static a_boolean compare_dynamic_inits( a_dynamic_init_ptr dip1, a_dynamic_init_ptr dip2, a_compare_constants_options_set options) /* Return TRUE if dip1 and dip2 are equivalent dynamic initializations. options is a set of flags that control the way in which certain comparisons are done. */ { a_boolean eq = FALSE; if (dip1 == NULL && dip2 == NULL) { eq = TRUE; } else if (dip1 == NULL || dip2 == NULL) { /* Not equal. */ } else if (dip1->kind == dip2->kind && dip1->static_temp == dip2->static_temp && dip1->has_temporary_lifetime == dip2->has_temporary_lifetime && same_entities(dip1->variable, dip2->variable) && same_entities(dip1->destructor, dip2->destructor)) { switch (dip1->kind) { case dik_none: case dik_zero: eq = TRUE; break; case dik_constant: case dik_nonconstant_aggregate: eq = compare_constants(dip1->variant.constant.ptr, dip2->variant.constant.ptr, options); break; case dik_expression: case dik_class_result_via_ctor: eq = compare_expressions(dip1->variant.expression, dip2->variant.expression, options); break; case dik_constructor: eq = (same_entities(dip1->variant.constructor.ptr, dip2->variant.constructor.ptr) && dip1->variant.constructor.value_initialization == dip2->variant.constructor.value_initialization && compare_expression_lists(dip1->variant.constructor.args, dip2->variant.constructor.args, options)); break; default: unexpected_condition_str("compare_dynamic_inits: bad kind"); } /* switch */ } /* if */ return eq; } /* compare_dynamic_inits */ static an_itf_flag_set itf_flags_for_cc_options( a_compare_constants_options_set options) /* Return the appropriate type comparison flags for a given set of constant comparison options. */ { an_itf_flag_set itf_options = ITF_NO_FLAGS; if ((options & CC_EXACT_EQUIVALENCE) != 0 || (options & CC_STRICTLY_IDENTICAL) != 0) { itf_options |= ITF_EXACT_EQUIVALENCE; } /* if */ if (options & CC_EXACT_DECLTYPE_EXPR_MATCH_REQUIRED) { itf_options |= ITF_EXACT_DECLTYPE_EXPR_MATCH_REQUIRED; } /* if */ return itf_options; } /* itf_flags_for_cc_options */ static an_equiv_templ_arg_options_set eta_flags_for_cc_options( a_compare_constants_options_set options) /* Return the appropriate template argument list comparison flags for a given set of constant comparison options. */ { an_equiv_templ_arg_options_set eta_options = ETA_NO_OPTIONS; if ((options & CC_EXACT_EQUIVALENCE) != 0 || (options & CC_STRICTLY_IDENTICAL) != 0) { eta_options |= ETA_EXACT_MATCH_REQUIRED; } /* if */ if (options & CC_EXACT_DECLTYPE_EXPR_MATCH_REQUIRED) { eta_options |= ETA_EXACT_DECLTYPE_EXPR_MATCH_REQUIRED; } /* if */ return eta_options; } /* eta_flags_for_cc_options */ static a_boolean equiv_template_variables( a_variable_ptr var1, a_variable_ptr var2, a_compare_constants_options_set options) /* var1 and var2 are variable template instances or template static data members. If they are variable template instances, compare the template argument lists and return TRUE if they match. Otherwise, return FALSE (including all cases where one or both are template static data members). */ { a_boolean result = FALSE; a_variable_template_info_ptr vtip1 = var1->template_info; a_variable_template_info_ptr vtip2 = var2->template_info; a_template_arg_ptr tap1 = vtip1->template_arg_list; a_template_arg_ptr tap2 = vtip2->template_arg_list; if (tap1 == NULL || tap2 == NULL) { /* At least one of the variables is not a variable template instance. */ } else { /* Check whether both template variables are based on the same variable template, and if so, whether the template arguments are equivalent. */ an_equiv_templ_arg_options_set eta_options; a_symbol_ptr template_sym1; a_symbol_ptr template_sym2; a_boolean exact_templ_arg_match_required; a_boolean exact_decltype_exprs_required; a_boolean exact_nesting_depths_required; template_sym1 = symbol_for(vtip1->assoc_template); template_sym2 = symbol_for(vtip2->assoc_template); check_assertion(template_sym1 != NULL && template_sym2 != NULL); eta_options = eta_flags_for_cc_options(options); exact_templ_arg_match_required = (eta_options & ETA_EXACT_MATCH_REQUIRED) != 0; exact_decltype_exprs_required = (eta_options & ETA_EXACT_DECLTYPE_EXPR_MATCH_REQUIRED) != 0; exact_nesting_depths_required = !(eta_options & ETA_ALLOW_EQUIV_NESTING_DEPTHS); if (equiv_templates_and_arg_lists( template_sym1, template_sym2, &var1->source_corresp, &var2->source_corresp, tap1, tap2, eta_options, /*error_matches_anything=*/FALSE, exact_templ_arg_match_required, exact_decltype_exprs_required, exact_nesting_depths_required)) { result = TRUE; } /* if */ } /* if */ return result; } /* equiv_template_variables */ static a_boolean equiv_requires_expr_params(an_expr_node_ptr node1, an_expr_node_ptr node2) /* Return TRUE if the given requires-expressions have equivalent (possibly empty) parameter lists. */ { a_boolean result = TRUE; a_param_type_ptr ptp1 = node1->variant.requires_expr.parameters, ptp2 = node2->variant.requires_expr.parameters; for (; ptp1 != NULL && ptp2 != NULL; ptp1 = ptp1->next, ptp2 = ptp2->next) { if (!identical_types(ptp1->type, ptp2->type)) { result = FALSE; goto done; } /* if */ } /* for */ if (ptp1 != NULL || ptp2 != NULL) { result = FALSE; } /* if */ done: return result; } /* equiv_requires_expr_params */ static a_boolean equiv_requires_expr_substs(an_expr_node_ptr node1, an_expr_node_ptr node2) /* Return TRUE if the given requires-expressions have equivalent delayed template argument substitutions. */ { a_boolean result = TRUE; a_subst_pairs_array const &substs1 = requires_expr_substs->get(node1); a_subst_pairs_array const &substs2 = requires_expr_substs->get(node2); a_subst_pairs_descr const *spd1 = substs1.begin(); a_subst_pairs_descr const *spd2 = substs2.begin(); result = substs1.length() == substs2.length(); for (; result && spd1 != substs1.end(); ++spd1, ++spd2) { if (spd1->params != spd2->params || spd1->adjust_coordinates != spd2->adjust_coordinates || spd1->alias_deduction_guide != spd2->alias_deduction_guide) { result = FALSE; } else if (!equiv_template_arg_lists( spd1->args, spd2->args, (ETA_EXACT_MATCH_REQUIRED | ETA_EXACT_DECLTYPE_EXPR_MATCH_REQUIRED))) { result = FALSE; } /* if */ } /* for */ return result; } /* equiv_requires_expr_substs */ an_expr_node_ptr unwrap_if_tpck_expression(an_expr_node_ptr expr) /* If the given expression is for a constant of ck_template_param/tpck_expression kind, return the underlying expression. Otherwise return expr. Make an exception for enumerator constants that are represented as tpck_expression entries for the underlying (dependent) expression: A use of the enumerator constant should not be treated as equivalent to the underlying expression. */ { while (is_constant_node(expr)) { a_constant_ptr cp = node_constant(expr); if (constant_is(cp, ck_template_param) && tpck_is(cp, tpck_expression) && !cp->is_named_constant_definition) { expr = expr_node_from_tpck_expression(cp); expr = skip_parens(expr); } else { break; } /* if */ } /* while */ return expr; } /* unwrap_if_tpck_expression */ a_boolean compare_expressions(an_expr_node_ptr node1, an_expr_node_ptr node2, a_compare_constants_options_set options) /* Return TRUE if node1 and node2 are equivalent expression trees. options is a set of flags that control the way in which certain comparisons are done. */ { a_boolean eq = FALSE; an_itf_flag_set itf_options; #if DEBUG STATIC_THREAD size_t db_level = 0; db_level++; #endif /* DEBUG */ itf_options = itf_flags_for_cc_options(options); if (node1 == NULL && node2 == NULL) { eq = TRUE; goto done; } else if (node1 == NULL || node2 == NULL) { /* Not equal. */ goto done; } /* if */ /* An expression being wrapped in a ck_template_param/tpck_expression constant does not affect equivalence: Unwrap such cases for the comparison that follows. */ node1 = unwrap_if_tpck_expression(skip_parens(node1)); node2 = unwrap_if_tpck_expression(skip_parens(node2)); if (node1->kind == node2->kind && ((node1->is_lvalue == node2->is_lvalue && node1->is_xvalue == node2->is_xvalue) || (options & CC_GENERIC) != 0) && node1->is_pack_expansion == node2->is_pack_expansion && #if MICROSOFT_EXTENSIONS_ALLOWED node1->is_safe_cast == node2->is_safe_cast && #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ node1->is_static_cast == node2->is_static_cast) { /* Some basic properties of the expression nodes are equivalent. Note that in template contexts, glvalue-to-prvalue conversion may or may not have occurred and thus value category comparison may not be reliable. CC_GENERIC indicates that we are in such a context where value categories can differ. In such cases, types can also differ in their type qualifiers (see below). */ a_boolean do_type_comparison = TRUE; switch (node1->kind) { case enk_operation: if (node1->variant.operation.kind == node2->variant.operation.kind && node1->variant.operation.is_const_cast == node2->variant.operation.is_const_cast && node1->variant.operation.is_reinterpret_cast == node2->variant.operation.is_reinterpret_cast && #if MICROSOFT_EXTENSIONS_ALLOWED node1->variant.operation.requires_runtime_cast_check == node2->variant.operation.requires_runtime_cast_check && node1->variant.operation.is_tracking_reference_cast == node2->variant.operation.is_tracking_reference_cast && #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ node1->variant.operation.is_reference_cast == node2->variant.operation.is_reference_cast && node1->variant.operation.is_rvalue_reference_cast == node2->variant.operation.is_rvalue_reference_cast) { if (node1->compiler_generated != node2->compiler_generated) { break; } /* if */ an_expr_node_ptr op1 = node1->variant.operation.operands; an_expr_node_ptr op2 = node2->variant.operation.operands; check_assertion(op1 != NULL && op2 != NULL); if (node_operator_is(node1, eok_call)) { an_expr_node_ptr expr1 = op1; an_expr_node_ptr expr2 = op2; if (is_operation_node(expr1) && node_operator_is(expr1, eok_ref_indirect) && is_operation_node(expr2) && node_operator_is(expr2, eok_ref_indirect)) { /* Skip over similar reference indirection nodes. */ expr1 = expr1->variant.operation.operands; expr2 = expr2->variant.operation.operands; } /* if */ if (is_variable_node(expr1) && is_variable_node(expr2) && node_variable(expr1) != node_variable(expr2) && (options & CC_EXACT_DECLTYPE_EXPR_MATCH_REQUIRED)) { /* This is a "function call" to a variable, which can occur in a dependent expression. */ a_variable_ptr var1 = node_variable(expr1); a_variable_ptr var2 = node_variable(expr2); if (same_name(&var1->source_corresp, &var2->source_corresp) && identical_types_full(expr1->type, expr2->type, itf_options) && ((var1->is_template_variable && var2->is_template_variable && equiv_template_arg_lists( var1->template_info->template_arg_list, var2->template_info->template_arg_list, eta_flags_for_cc_options(options))) || (!var1->is_template_variable && !var2->is_template_variable))) { /* Although this is an ODR violation, the danger is minimal since the variables have the same types and the expressions appear in the operands of decltype operators, so the calls will not be executed. Treat the variables as matching and just check the argument lists. */ op1 = op1->next; op2 = op2->next; } /* if */ } /* if */ } /* if */ eq = compare_expression_lists(op1, op2, options); } /* if */ break; case enk_constant: eq = compare_constants(node_constant(node1), node_constant(node2), options); break; case enk_variable: { a_variable_ptr var1 = node_variable(node1); a_variable_ptr var2 = node_variable(node2); if (same_entities(var1, var2)) { eq = TRUE; } else if (var1->is_template_variable && var2->is_template_variable) { eq = equiv_template_variables(var1, var2, options); } else if ((options & CC_CONSTEXPR_NAME_EQUIVALENCES_ALLOWED) != 0 && var1->is_constexpr && var2->is_constexpr && (!is_glvalue_node(node1) || is_array_type(node1->type)) && (!is_glvalue_node(node2) || is_array_type(node2->type)) && same_name(&var1->source_corresp, &var2->source_corresp)) { /* This is an ODR violation, however, the usage is somewhat benign when the above conditions are satisfied. Consider the two variables "the same" when they have the same value. */ a_constant_ptr cp1, cp2; cp1 = initializer_constant(var1); cp2 = initializer_constant(var2); eq = compare_constants(cp1, cp2, options); } /* if */ /* Don't do the type comparison because we may be comparing variables from different translation units, one of which may have an incomplete array type. It's enough to know they are the same variable. */ do_type_comparison = FALSE; } break; case enk_routine: eq = same_entities(node_routine(node1), node_routine(node2)); break; case enk_field: eq = same_entities(node_field(node1), node_field(node2)); break; case enk_temp_init: eq = compare_dynamic_inits(node1->variant.init.dynamic_init, node2->variant.init.dynamic_init, options); break; case enk_new_delete: { a_new_delete_supplement_ptr ndsp1 = node1->variant.new_delete; a_new_delete_supplement_ptr ndsp2 = node2->variant.new_delete; eq = (ndsp1->is_new == ndsp2->is_new && ndsp1->placement_new == ndsp2->placement_new && ndsp1->array_delete == ndsp2->array_delete && identical_types_full(ndsp1->type, ndsp2->type, itf_options) && same_entities(ndsp1->routine, ndsp2->routine) && compare_expression_lists(ndsp1->arg, ndsp2->arg, options) && compare_dynamic_inits(ndsp1->dynamic_init, ndsp2->dynamic_init, options) && compare_expressions(ndsp1->number_of_elements, ndsp2->number_of_elements, options)); } break; #if MICROSOFT_EXTENSIONS_ALLOWED case enk_gcnew: { a_gcnew_supplement_ptr gsp1 = node1->variant.gcnew_info; a_gcnew_supplement_ptr gsp2 = node2->variant.gcnew_info; eq = (gsp1->is_cli_array == gsp2->is_cli_array && !(gsp1->is_cli_array && gsp1->has_new_initializer != gsp2->has_new_initializer) && node1->compiler_generated == node2->compiler_generated && identical_types_full(gsp1->type, gsp2->type, itf_options) && compare_expression_lists(gsp1->cli_array_dimension_lengths, gsp2->cli_array_dimension_lengths, options) && compare_dynamic_inits(gsp1->dynamic_init, gsp2->dynamic_init, options)); } break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case enk_lambda: eq = (node1->variant.init.source.lambda == node2->variant.init.source.lambda && node1->variant.init.dynamic_init == node2->variant.init.dynamic_init); break; case enk_throw: { a_throw_supplement_ptr tsp1 = node1->variant.throw_info; a_throw_supplement_ptr tsp2 = node2->variant.throw_info; if (tsp1 == NULL && tsp2 == NULL) { eq = TRUE; } else if (tsp1 == NULL || tsp2 == NULL) { eq = FALSE; } else { eq = (identical_types_full(tsp1->type, tsp2->type, itf_options) && compare_dynamic_inits(tsp1->dynamic_init, tsp2->dynamic_init, options)); } /* if */ } break; case enk_object_lifetime: eq = compare_expressions(node1->variant.object_lifetime.expr, node2->variant.object_lifetime.expr, options); break; case enk_typeid: eq = compare_expression_lists( node1->variant.typeid_info.type_with_opt_expr, node2->variant.typeid_info.type_with_opt_expr, options) && node1->variant.typeid_info.is_dynamic == node2->variant.typeid_info.is_dynamic; break; case enk_sizeof: case enk_datasizeof: case enk_alignof: eq = (node1->variant.sizeof_info.is_type == node2->variant.sizeof_info.is_type && (node1->variant.sizeof_info.is_type ? identical_types_full(node1->variant.sizeof_info.variant.type, node2->variant.sizeof_info.variant.type, itf_options) : compare_expressions( node1->variant.sizeof_info.variant.expr, node2->variant.sizeof_info.variant.expr, options))); break; case enk_sizeof_pack: eq = (node1->variant.sizeof_pack.is_type == node2->variant.sizeof_pack.is_type && node1->variant.sizeof_pack.is_template_template == node2->variant.sizeof_pack.is_template_template && (node1->variant.sizeof_pack.is_template_template ? (node1->variant.sizeof_pack.variant.templ == node2->variant.sizeof_pack.variant.templ) : (node1->variant.sizeof_pack.is_type ? identical_types_full( node1->variant.sizeof_pack.variant.type, node2->variant.sizeof_pack.variant.type, itf_options) : compare_expressions( node1->variant.sizeof_pack.variant.expr, node2->variant.sizeof_pack.variant.expr, options)))); break; case enk_reuse_value: eq = compare_dynamic_inits(node1->variant.reused_value_init, node2->variant.reused_value_init, options); break; case enk_address_of_ellipsis: eq = TRUE; break; case enk_statement: /* GNU statement expression. If the statement pointers are not identical, the statements are not identical. */ eq = (node1->variant.statement == node2->variant.statement); break; case enk_type_operand: eq = identical_types_full(type_operand_type(node1), type_operand_type(node2), itf_options); break; case enk_builtin_operation: if (node1->variant.builtin_operation.kind == node2->variant.builtin_operation.kind) { eq = compare_expression_lists( node1->variant.builtin_operation.operands, node2->variant.builtin_operation.operands, options); } /* if */ break; case enk_param_ref: /* For two parameter references to be equivalent, they must refer to the same parameter number at the same level in the stack of function prototype scopes. */ eq = node1->variant.param_ref.param_num == node2->variant.param_ref.param_num && node1->variant.param_ref.levels_up == node2->variant.param_ref.levels_up; break; case enk_braced_init_list: eq = compare_expression_lists(node1->variant.braced_init_list, node2->variant.braced_init_list, options); break; case enk_c11_generic: eq = compare_expressions(node1->variant.c11_generic.result, node2->variant.c11_generic.result, options); break; #if BUILTIN_FUNCTIONS_ENABLED case enk_builtin_choose_expr: { an_expr_node_ptr op1 = node1->variant.builtin_choose_expr.operands, op2 = node2->variant.builtin_choose_expr.operands; op1 = node1->variant.builtin_choose_expr.choose_first ? op1->next : op1->next->next; op2 = node2->variant.builtin_choose_expr.choose_first ? op2->next : op2->next->next; eq = compare_expressions(op1, op2, options); } break; #endif /* BUILTIN_FUNCTIONS_ENABLED */ case enk_fold: { an_expr_node_ptr op1 = node1->variant.fold.operands, op2 = node2->variant.fold.operands; eq = node1->variant.fold.left_associative == node2->variant.fold.left_associative && node1->variant.fold.operator_token == node2->variant.fold.operator_token && compare_expressions(op1, op2, options) && (op1->next == NULL ? op2->next == NULL : (op2->next != NULL && compare_expressions(op1->next, op2->next, options))); } break; case enk_concept_id: eq = same_entities(node1->variant.concept_id.concept_template, node2->variant.concept_id.concept_template) && equiv_template_arg_lists(node1->variant.concept_id.args, node2->variant.concept_id.args, (ETA_IS_NONREAL_MEMBER | eta_flags_for_cc_options(options))); break; case enk_requires: eq = compare_expression_lists( node1->variant.requires_expr.requirements, node2->variant.requires_expr.requirements, options) && equiv_requires_expr_params(node1, node2) && (!(options & CC_EXACT_EQUIVALENCE) || equiv_requires_expr_substs(node1, node2)); break; case enk_compound_req: eq = compare_expression_lists( node1->variant.compound_req.expr_and_constraint, node2->variant.compound_req.expr_and_constraint, options) && node1->variant.compound_req.is_noexcept == node2->variant.compound_req.is_noexcept; break; case enk_nested_req: eq = compare_expressions(node1->variant.nested_req.constraint, node2->variant.nested_req.constraint, options); break; case enk_const_eval_deferred: { a_const_eval_reattempt_state &state1 = node1->variant.const_eval_deferred.reattempt_state; a_const_eval_reattempt_state &state2 = node2->variant.const_eval_deferred.reattempt_state; if ((state1.default_arg == state2.default_arg) && (state1.default_mem_init == state2.default_mem_init)) { an_expr_node_ptr wrapped1 = node1->variant.const_eval_deferred.wrapped; an_expr_node_ptr wrapped2 = node2->variant.const_eval_deferred.wrapped; eq = compare_expressions(wrapped1, wrapped2, options); } /* if */ } break; case enk_template_name: eq = node1->variant.template_name == node2->variant.template_name; break; case enk_pack_index: eq = compare_expressions(node1->variant.pack_index.expr, node2->variant.pack_index.expr, options) && compare_expressions(node1->variant.pack_index.index_expr, node2->variant.pack_index.index_expr, options); break; case enk_error: /* Nonequivalence is assumed. */ break; case enk_condition: #if VLA_DEALLOCATIONS_IN_IL case enk_vla_dealloc: #endif /* VLA_DEALLOCATIONS_IN_IL */ #if DO_IL_LOWERING && ABI_CHANGES_FOR_COVARIANT_VIRTUAL_FUNC_RETURN case enk_result_of_overriding_function: #endif /* DO_IL_LOWERING && ABI_CHANGES_FOR_COVARIANT_VIRTUAL_FUNC_RETURN */ case enk_yield: case enk_await: case enk_initializer: case enk_token_sequence: case enk_reclaimed: case enk_last: unexpected_condition_str("compare_expressions: bad expr kind"); break; default_is_unexpected(); } /* switch */ if (eq && do_type_comparison) { if ((node1->is_lvalue != node2->is_lvalue || node1->is_xvalue != node2->is_xvalue)) { /* In generic contexts, the value category may be unreliable, and in turn that may mean some qualifiers are dropped on one node but not the other. We still want to check the types, because they may contain components that have different SFINAE behavior, such as decltype(T::x) vs. decltype(T::y). */ itf_options |= ITF_IGNORE_TOP_LEVEL_QUALIFIERS; } /* if */ if (!identical_types_full(node1->type, node2->type, itf_options)) { eq = FALSE; } /* if */ } /* if */ if (eq && (options & CC_EXACT_EQUIVALENCE) && (node1->extra.rescan_info != NULL) != (node2->extra.rescan_info != NULL)) { /* The two expressions are equivalent, but one has associated rescan information and the other not. With the given option, the two should not be considered equivalent because it could cause the version with rescan information to be replaced by the other version, thereby causing spurious rescan failures. (If the two nodes have associated rescan information, that information can be assumed to be equivalent since the expressions are equivalent.) This check is done even when the type comparison is skipped (e.g., for variable references). */ eq = FALSE; } /* if */ } /* if */ done: #if DEBUG db_level--; if (!eq && db_flag_is_set("compare_expressions") && node1 != NULL) { fprintf(f_debug, "compare_expressions:\n"); fprintf(f_debug, "\n%*s%s (%lu/%lu)\n", (int)db_level, "", "expr1: ", (unsigned long)node1->position.seq, (unsigned long)node1->position.column); db_expr_node(node1, db_level); if (node2 != NULL) { fprintf(f_debug, "\n%*s%s (%lu/%lu)\n", (int)db_level, "", "expr2: ", (unsigned long)node2->position.seq, (unsigned long)node2->position.column); db_expr_node(node2, db_level); fprintf(f_debug, "\n"); } /* if */ } /* if */ #endif /* DEBUG */ return eq; } /* compare_expressions */ extern a_boolean equiv_requires_clauses(a_requires_clause_ptr rcp1, a_requires_clause_ptr rcp2) /* Return TRUE if the given requires clauses (possibly NULL) are equivalent. Otherwise, return FALSE. */ { a_boolean result; if (rcp1 == NULL || rcp2 == NULL) { result = rcp1 == rcp2; } else { result = compare_expressions(rcp1->constraint, rcp2->constraint, CC_EXACT_DECLTYPE_EXPR_MATCH_REQUIRED); } /* if */ return result; } /* equiv_requires_clauses */ static a_boolean equiv_template_constant_identity( a_constant *cp1, a_constant *cp2, a_compare_constants_options_set options) /* Compare two ck_template_param constants that represent dependent entities to see if they have the same name and parent ("identity") information. Return TRUE if so. options is a set of flags that control the way in which certain comparisons are done. */ { a_boolean eq; check_assertion(cp1->kind == (a_constant_repr_kind)ck_template_param && cp2->kind == (a_constant_repr_kind)ck_template_param); if (cp1->source_corresp.name == cp2->source_corresp.name && cp1->source_corresp.is_class_member == cp2->source_corresp.is_class_member && cp1->source_corresp.member_of_unknown_base == cp2->source_corresp.member_of_unknown_base && (!(options & (CC_STRICTLY_IDENTICAL | CC_EXACT_EQUIVALENCE)) || cp1->source_corresp.qualified_unknown_base_member == cp2->source_corresp.qualified_unknown_base_member)) { if (cp1->variant.template_param.kind == (a_template_param_constant_kind)tpck_unknown_function && !cp1->variant.template_param.is_qualified_name) { /* An unknown function referred to without a qualified name: Any parent information is dependent on context and should not be compared. */ eq = TRUE; } else { /* Compare parent information. */ an_itf_flag_set itf_options; itf_options = itf_flags_for_cc_options(options); eq = (cp1->source_corresp.is_class_member ? identical_types_full(parent_class_of(cp1), parent_class_of(cp2), itf_options) : corresponding_namespaces( /*lint !e666*/parent_namespace_or_null(cp1), /*lint !e666*/parent_namespace_or_null(cp2))); } /* if */ } else { eq = FALSE; } /* if */ return eq; } /* equiv_template_constant_identity */ a_boolean compare_reflections(a_reflection_value rv1, a_reflection_value rv2, a_compare_constants_options_set options) /* Return TRUE if the two given reflection values are equivalent. options is a set of flags that control the way in which certain comparisons are done. See the definition of the CC flags in il.h for more information. */ { a_boolean eq; strip_template_arg(&rv1); strip_template_arg(&rv2); if (rv1.entity.kind != rv2.entity.kind) { eq = FALSE; } else if (rv1.entity.kind == iek_type) { a_type_ptr tp1 = (a_type*)rv1.entity.ptr, tp2 = (a_type*)rv2.entity.ptr; if (type_is_typedef(tp1) || type_is_typedef(tp2)) { eq = (tp1 == tp2); } else { eq = identical_types(tp1, tp2); } /* if */ } else if (rv1.entity.kind == iek_constant) { eq = compare_constants((a_constant*)rv1.entity.ptr, (a_constant*)rv2.entity.ptr, options); } else { eq = (rv1.entity.ptr == rv2.entity.ptr); } /* if */ return eq; } /* compare_reflections */ static a_boolean equiv_subobject_paths(a_subobject_path *spp1, a_subobject_path *spp2) /* Return TRUE if the given subobject paths are equivalent. */ { a_boolean result = TRUE; while (spp1 != NULL && spp2 != NULL && result) { if (spp1->is_offset != spp2->is_offset || spp1->is_base_class != spp2->is_base_class) { result = FALSE; } else if (spp1->is_offset) { if (spp1->variant.ptr_offset != spp2->variant.ptr_offset) { result = FALSE; } /* if */ } else if (spp1->is_base_class) { if (!same_base_classes(spp1->variant.base_class, spp2->variant.base_class)) { result = FALSE; } /* if */ } else { if (!same_entities(spp1->variant.field, spp2->variant.field)) { result = FALSE; } /* if */ } /* if */ spp1 = spp1->next; spp2 = spp2->next; } /* while */ return result; } /* equiv_subobject_paths */ static a_boolean compare_constants_list(a_constant_ptr list1, a_constant_ptr list2, a_compare_constants_options_set options) /* Return TRUE if both lists point to the same number of constants, and each respective pair produces a TRUE value for a call to compare_constants with the given options. */ { a_boolean eq = TRUE; for (; list1 != NULL && list2 != NULL; list1 = list1->next, list2 = list2->next) { if (!compare_constants(list1, list2, options)) { eq = FALSE; break; } /* if */ } /* for */ if (list1 != NULL || list2 != NULL) { eq = FALSE; } /* if */ return eq; } /* compare_constants_list */ a_boolean compare_constants(a_constant_ptr cp1, a_constant_ptr cp2, a_compare_constants_options_set options) /* Return TRUE if the two constants are identical. options is a set of flags that control the way in which certain comparisons are done. See the definition of the CC flags in il.h for more information. */ { a_boolean eq = FALSE; a_type_ptr cp1_type = skip_lexical_typerefs(cp1->type), cp2_type = skip_lexical_typerefs(cp2->type); a_boolean same_types = FALSE, strictly_identical; an_itf_flag_set itf_options; if (cp1 == cp2) { eq = TRUE; goto end_of_routine; } else if (cp1->kind != cp2->kind) { /* eq = FALSE; */ goto end_of_routine; } /* if */ strictly_identical = (options & CC_STRICTLY_IDENTICAL) != 0; itf_options = itf_flags_for_cc_options(options); if (cp1_type == NULL || cp2_type == NULL) { /* Some constant kinds have null types (e.g., ck_init_repeat). */ same_types = (cp1_type == cp2_type); #if MICROSOFT_EXTENSIONS_ALLOWED } else if ( cli_or_cx_enabled && constant_is(cp1, ck_address) && (cp1->variant.address.kind == (an_address_base_kind)abk_cli_array || cp2->variant.address.kind == (an_address_base_kind)abk_cli_array)) { /* C++/CLI array constants, which are only valid in C++/CLI custom attribute argument expressions, are not comparable. */ goto end_of_routine; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else if (!strictly_identical) { /* The types must be "the same", but it is sufficient that they be compatible (don't use the types_are_compatible or types_are_redecl_compatible macros because we don't want errors to be considered compatible with everything, but we do need a[] and a[3] to be considered compatible, because in two translation units the types might be different in that way). */ a_type_ptr eff_cp1_type = skip_typerefs_not_dependent_decltypes(cp1_type); a_type_ptr eff_cp2_type = skip_typerefs_not_dependent_decltypes(cp2_type); /* For address constants, compare the types underneath the pointer types so that address of a[] and address of a[3] are considered compatible. */ if (constant_is(cp1, ck_address) && cp1->variant.address.kind == (an_address_base_kind)abk_variable && cp2->variant.address.kind == (an_address_base_kind)abk_variable && !cp1->implicit_cast && !cp2->implicit_cast && is_pointer_type(eff_cp1_type) && is_pointer_type(eff_cp2_type)) { eff_cp1_type = type_pointed_to(eff_cp1_type); eff_cp1_type = skip_typerefs_not_dependent_decltypes(eff_cp1_type); eff_cp2_type = type_pointed_to(eff_cp2_type); eff_cp2_type = skip_typerefs_not_dependent_decltypes(eff_cp2_type); } /* if */ if (gpp_mode && cp1_type->kind != cp2_type->kind && cp2_type->kind == (a_type_kind)tk_template_param) { /* g++ and clang allow some dependent types that are specified differently to match between declarations. See check_gpp_template_redecl_match for more information. */ same_types = check_gpp_template_redecl_match(cp1_type, eff_cp2_type); } /* if */ if (!same_types && (options & CC_TEMPLATE_TEMPLATE_PARAM) != 0) { /* When matching a template template parameter constant, consider this a match if the second type is from a template template parameter. This is accepted in non-Microsoft mode and for certain g++/clang versions when the first type is from a template parameter. */ a_boolean cp1_is_tt_templ_param; a_boolean cp2_is_tt_templ_param; cp2_is_tt_templ_param = (is_template_param_type(eff_cp2_type) && eff_cp2_type->kind == (a_type_kind)tk_template_param && eff_cp2_type->variant.template_param.kind == (a_template_param_type_kind)tptk_param && eff_cp2_type->variant.template_param.extra_info-> coordinates.depth == NO_NESTING_DEPTH); cp1_is_tt_templ_param = is_template_param_type(eff_cp1_type) && eff_cp1_type->kind == (a_type_kind)tk_template_param && eff_cp1_type->variant.template_param.kind == (a_template_param_type_kind)tptk_param && eff_cp1_type->variant.template_param.extra_info-> coordinates.depth == NO_NESTING_DEPTH; same_types = cp2_is_tt_templ_param || ((!gpp_mode || (gpp_version_is(>= 60000) || clang_version_is(>= 30800))) && !microsoft_mode && cp1_is_tt_templ_param); } /* if */ if (!same_types) { same_types = f_types_are_compatible(eff_cp1_type, eff_cp2_type, TCF_REDECLARATION); } /* if */ } else { /* strictly_identical is TRUE. */ /* The types must be pointer-identical. */ same_types = same_entities(cp1_type, cp2_type); if (!same_types && constant_is(cp1, ck_string) && identical_types_full(cp1_type, cp2_type, itf_options)) { /* ... except that for string constants we allow type equivalence, because for strings with length greater than MAX_TRACKED_STRING_TYPE_LENGTH a different array type is created each time. Note that, while this test is desirable in general, it is necessary for the IA-64 ABI so that identical string literals in inline functions are properly merged into one. Without this test we would put out two variables for the two string constants and they would have the same mangled name. */ same_types = TRUE; } /* if */ } /* if */ /* The constants can be the same only if their types are the same. */ if (same_types) { switch (cp1->kind) { case ck_error: #if UPC_EXTENSIONS_ALLOWED case ck_upc_mythread: #endif /* UPC_EXTENSIONS_ALLOWED */ case ck_void: /* No further field to check. */ eq = TRUE; break; #if UPC_EXTENSIONS_ALLOWED case ck_upc_threads: #endif /* UPC_EXTENSIONS_ALLOWED */ case ck_integer: eq = (cmp_integer_constants(cp1, cp2) == 0); if (eq && strictly_identical) { if (cp1->non_arithmetic != cp2->non_arithmetic || cp1->nullptr_keyword != cp2->nullptr_keyword #if MICROSOFT_EXTENSIONS_ALLOWED || cp1->native_nullptr_keyword != cp2->native_nullptr_keyword #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if GNU_EXTENSIONS_ALLOWED || cp1->null_keyword != cp2->null_keyword #endif /* GNU_EXTENSIONS_ALLOWED */ ) eq = FALSE; } /* if */ break; #if FIXED_POINT_ALLOWED case ck_fixed_point: eq = (cmp_fixed_point_constants(cp1, cp2) == 0); break; #endif /* FIXED_POINT_ALLOWED */ case ck_string: if (cp1->variant.string.length == cp2->variant.string.length) { eq = #if DO_IL_LOWERING && ASSIGN_STRING_LITERAL_SEQUENCE_NUMBERS /* The sequence numbers must match, when present. */ cp1->variant.string.sequence_number == cp2->variant.string.sequence_number && #endif /* DO_IL_LOWERING && ASSIGN_STRING_LITERAL_SEQUENCE_NUMBERS */ (memcmp(cp1->variant.string.value, cp2->variant.string.value, size_t_arg(cp1->variant.string.length)) == 0); } /* if */ break; case ck_float: #if C99_IL_EXTENSIONS_SUPPORTED case ck_imaginary: #endif /* C99_IL_EXTENSIONS_SUPPORTED */ cp1_type = skip_typerefs(cp1_type); if (is_floating_type(cp1_type)) { eq = fp_same_representation(cp1_type->variant.float_kind, &cp1->variant.float_value, &cp2->variant.float_value); } /* if */ break; #if C99_IL_EXTENSIONS_SUPPORTED case ck_complex: cp1_type = skip_typerefs(cp1_type); if (is_floating_type(cp1_type)) { eq = (fp_same_representation(cp1_type->variant.float_kind, &cp1->variant.complex_value->real, &cp2->variant.complex_value->real) && fp_same_representation(cp1_type->variant.float_kind, &cp1->variant.complex_value->imag, &cp2->variant.complex_value->imag)); } /* if */ break; #endif /* C99_IL_EXTENSIONS_SUPPORTED */ case ck_address: if (cp1->variant.address.kind == cp2->variant.address.kind && cp1->variant.address.offset == cp2->variant.address.offset && cp1->variant.address.is_object_reflection == cp2->variant.address.is_object_reflection) { /* An object reflection (as produced by std::meta::reflect_object and friends) never compares equal to a reflection of a pointer value that happens to hold the same address, even though both are ck_address entries: the is_object_reflection flag distinguishes them. */ switch (cp1->variant.address.kind) { case abk_routine: eq = corresponding_routines( cp1->variant.address.variant.routine, cp2->variant.address.variant.routine); break; case abk_variable: eq = corresponding_variables( cp1->variant.address.variant.variable, cp2->variant.address.variant.variable); break; case abk_constant: if (options & CC_RELAXED_ADDRESS_OF_CONSTANT_COMPARISON) { eq = compare_constants(cp1->variant.address.variant.constant, cp2->variant.address.variant.constant, options); } else { eq = (cp1->variant.address.variant.constant == cp2->variant.address.variant.constant); } /* if */ break; case abk_temporary: eq = (cp1->variant.address.variant.constant == cp2->variant.address.variant.constant); break; #if MICROSOFT_EXTENSIONS_ALLOWED case abk_uuidof: /* Microsoft __uuidof. */ { a_type_ptr uuid_type1 = cp1->variant.address.variant.type; a_type_ptr uuid_type2 = cp2->variant.address.variant.type; if (uuid_type1 == NULL && uuid_type2 == NULL) { eq = TRUE; } else if (uuid_type1 == NULL || uuid_type2 == NULL) { /* eq = FALSE; -- already set. */ } else { /* Compare the uuid strings. */ a_const_char *string1 = uuid_string_of_type(uuid_type1); a_const_char *string2 = uuid_string_of_type(uuid_type2); eq = (string1 == string2) || (string1 != NULL && string2 != NULL && strcmp(string1, string2) == 0); } /* if */ } break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case abk_typeid: #if MICROSOFT_EXTENSIONS_ALLOWED case abk_cli_typeid: #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ eq = identical_types_full(cp1->variant.address.variant.type, cp2->variant.address.variant.type, itf_options); break; case abk_label: eq = (cp1->variant.address.variant.label == cp2->variant.address.variant.label); break; case abk_param_ref: eq = cp1 == cp2; break; default: unexpected_condition_str( "compare_constants: bad address constant kind"); } /* switch */ if ((options & CC_TEMPLATE_ARG) && eq) { /* When comparing template arguments, the subobject paths must be equivalent. For example, with: struct S { int i, j; } s; template struct X; X<&s.i+1> and X<&s.j> are different types (and mangled differently) even though they may involve identical offsets. */ eq = equiv_subobject_paths(cp1->variant.address.subobject_path, cp2->variant.address.subobject_path); } /* if */ } /* if */ break; case ck_ptr_to_member: if (cp1->variant.ptr_to_member.is_function_ptr == cp2->variant.ptr_to_member.is_function_ptr) { if (cp1->variant.ptr_to_member.is_function_ptr) { eq = corresponding_routines( cp1->variant.ptr_to_member.variant.routine, cp2->variant.ptr_to_member.variant.routine); } else { eq = corresponding_fields( cp1->variant.ptr_to_member.variant.field, cp2->variant.ptr_to_member.variant.field); } /* if */ } /* if */ break; case ck_aggregate: eq = compare_constants_list(cp1->variant.aggregate.first_constant, cp2->variant.aggregate.first_constant, options); break; case ck_init_repeat: eq = compare_constants(cp1->variant.init_repeat.constant, cp2->variant.init_repeat.constant, options) && (cp1->variant.init_repeat.count == cp2->variant.init_repeat.count) && (cp1->variant.init_repeat. multidimensional_aggr_tail_not_repeated == cp2->variant.init_repeat. multidimensional_aggr_tail_not_repeated); break; case ck_dynamic_init: eq = compare_dynamic_inits(cp1->variant.dynamic_init.ptr, cp2->variant.dynamic_init.ptr, options); break; #if GNU_EXTENSIONS_ALLOWED case ck_label_difference: eq = compare_constants(cp1->variant.label_difference.from_address, cp2->variant.label_difference.from_address, options) && compare_constants(cp1->variant.label_difference.to_address, cp2->variant.label_difference.to_address, options); break; #endif /* GNU_EXTENSIONS_ALLOWED */ #if DO_IL_LOWERING && GENERATE_EH_TABLES && !DO_FULL_PORTABLE_EH_LOWERING case ck_stack_offset: eq = (cp1->variant.stack_offset.variable == cp2->variant.stack_offset.variable && cp1->variant.stack_offset.offset == cp2->variant.stack_offset.offset); break; #endif /* DO_IL_LOWERING && ... */ case ck_template_param: /* Note that the constant types have been compared above. */ if (cp1->variant.template_param.kind == cp2->variant.template_param.kind) { /* Relax the comparing of underlying expressions somewhat (e.g., value categories are not reliable for these constants because glvalue-to-prvalue conversion may or may not yet have been applied). */ options |= CC_GENERIC; switch (cp1->variant.template_param.kind) { /* Don't compare coordinates when CC_TEMPLATE_TEMPLATE_PARAM is specified. */ case tpck_param: eq = (options & CC_EXACT_EQUIVALENCE) == 0 && ((options & CC_TEMPLATE_TEMPLATE_PARAM) != 0 || (cp1-> variant.template_param.variant.coordinates.position == cp2-> variant.template_param.variant.coordinates.position && equiv_nesting_depths( cp1->variant.template_param.variant.coordinates.depth, cp2->variant.template_param.variant.coordinates.depth))); break; case tpck_expression: eq = compare_expressions(expr_node_from_tpck_expression(cp1), expr_node_from_tpck_expression(cp2), options); break; case tpck_member: eq = equiv_template_constant_identity(cp1, cp2, options); break; case tpck_unknown_function: if (equiv_template_constant_identity(cp1, cp2, options) && (!strictly_identical || (cp1->variant.template_param.variant. unknown_function.symbol == cp2->variant.template_param.variant. unknown_function.symbol)) && cp1->variant.template_param.variant. unknown_function.opname_kind == cp2->variant.template_param.variant. unknown_function.opname_kind && cp1->variant.template_param.is_qualified_name == cp2->variant.template_param.is_qualified_name) { a_type_ptr tp1 = cp1->variant.template_param.variant. unknown_function.conversion_type; a_type_ptr tp2 = cp2->variant.template_param.variant. unknown_function.conversion_type; eq = TRUE; if (tp1 != NULL && tp2 != NULL) { if (!identical_types_full(tp1, tp2, itf_options)) eq = FALSE; } else if (!(tp1 == NULL && tp2 == NULL)) { eq = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (cp1->variant.template_param.variant.unknown_function .property_or_event_descr != cp2->variant.template_param.variant.unknown_function .property_or_event_descr || cp1->variant.template_param.variant.unknown_function .special_kind != cp2->variant.template_param.variant.unknown_function .special_kind) { eq = FALSE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ } /* if */ break; case tpck_address: case tpck_dependent_constant: eq = compare_constants(cp1->variant.template_param .variant.constant, cp2->variant.template_param .variant.constant, options); break; case tpck_concat_string_literals: eq = compare_constants_list(cp1->variant.template_param .variant.string_literal_list, cp2->variant.template_param .variant.string_literal_list, options); break; case tpck_sizeof: case tpck_datasizeof: case tpck_alignof: case tpck_uuidof: case tpck_typeid: case tpck_noexcept: eq = identical_types_full( cp1->variant.template_param.variant.templ_sizeof.type, cp2->variant.template_param.variant.templ_sizeof.type, itf_options); if (eq) { an_expr_node_ptr expr1 = generic_sizeof_arg_expr(cp1); an_expr_node_ptr expr2 = generic_sizeof_arg_expr(cp2); if (expr1 == NULL && expr2 == NULL) { eq = TRUE; } else if (expr1 == NULL || expr2 == NULL) { eq = FALSE; } else { eq = compare_expressions(expr1, expr2, options); } /* if */ } /* if */ break; case tpck_template_ref: eq = compare_constants(cp1->variant.template_param.variant. template_ref.con, cp2->variant.template_param.variant. template_ref.con, options) && equiv_template_arg_lists( cp1->variant.template_param.variant. template_ref.arg_list, cp2->variant.template_param.variant. template_ref.arg_list, (ETA_IS_NONREAL_MEMBER | eta_flags_for_cc_options(options))); break; case tpck_integer_pack: eq = compare_constants(cp1->variant.template_param.variant.bound, cp2->variant.template_param.variant.bound, options); break; case tpck_destructor: eq = identical_types_full( cp1->variant.template_param.variant.destructor.type, cp2->variant.template_param.variant.destructor.type, itf_options); break; default: unexpected_condition_str( "compare_constants: bad templ param const kind"); } /* switch */ } /* if */ if (eq && (options & CC_EXACT_EQUIVALENCE) != 0) { /* If the constants refer to local expression nodes, ensure they're both in the same function. (E.g., we do not want to reuse a constant that refers to a local expression for the nontype template argument of a template instance used in a different function. */ if (cp1->variant.template_param.local_expr_ref != cp2->variant.template_param.local_expr_ref || (cp1->variant.template_param.local_expr_ref && !same_entities(cp1->source_corresp.enclosing_routine, cp2->source_corresp.enclosing_routine))) { eq = FALSE; } /* if */ } /* if */ break; case ck_designator: if (cp1->variant.designator.is_generic != cp2->variant.designator.is_generic || cp1->variant.designator.is_field_designator != cp2->variant.designator.is_field_designator) { eq = FALSE; } else { if (cp1->variant.designator.is_generic) { if (cp1->variant.designator.is_field_designator) { eq = strcmp(cp1->variant.designator.variant.field_name, cp2->variant.designator.variant.field_name) == 0; } else { a_constant *idx1 = cp1->variant.designator.variant.subscript, *idx2 = cp2->variant.designator.variant.subscript; eq = compare_constants_list(idx1, idx2, options); } /* if */ } else { if (cp1->variant.designator.is_field_designator) { eq = same_entities(cp1->variant.designator.variant.field, cp2->variant.designator.variant.field); } else { eq = cp1->variant.designator.variant.array_element == cp2->variant.designator.variant.array_element; } /* if */ } /* if */ } /* if */ break; case ck_reflection: eq = compare_reflections(cp1->variant.reflection, cp2->variant.reflection, options); break; default: unexpected_condition_str("compare_constants: bad constant kind"); } /* switch */ } /* if */ end_of_routine: return eq; } /* compare_constants */ static a_boolean identical_constants(a_constant *cp1, a_constant *cp2) /* Return TRUE if the two constants are identical. This routine is called to decide whether two constant entries are sufficiently alike to be shared, such that only one of them need appear in the IL. */ { a_boolean eq = compare_constants(cp1, cp2, CC_STRICTLY_IDENTICAL); return eq; } /* identical_constants */ a_boolean eq_constants(a_constant *cp1, a_constant *cp2) /* Return TRUE if the two constants are equivalent, i.e., represent the same value. Thus, "(int)5" and "(const int)5" are equivalent -- even though they would not be considered "identical", since the type qualifiers are different. */ { a_boolean eq = compare_constants(cp1, cp2, CC_NO_OPTIONS); return eq; } /* eq_constants */ a_boolean expr_tree_contains_template_param_constant(an_expr_node_ptr node, a_constant_ptr cp) /* cp is either a pointer to a simple template parameter constant or else NULL (indicating any template param constant will do). If cp is NULL, return TRUE if node is or contains any template parameter constant. If it is not NULL, return TRUE if node refers to that particular constant directly or contains it among its operands (in a position that can be deduced from). */ { a_boolean found = FALSE; a_constant_ptr cp2; an_expr_node_ptr op; check_assertion(cp == NULL || cp->variant.template_param.kind == (a_template_param_constant_kind)tpck_param); node = skip_parens(node); if (node->kind == (an_expr_node_kind)enk_constant) { cp2 = node_constant(node); if (constant_is(cp2, ck_template_param)) { if (cp == NULL) { found = TRUE; } else { found = eq_constants(cp, cp2); } /* if */ } /* if */ } else if (node->kind == (an_expr_node_kind)enk_operation && cp == NULL) { for (op = node->variant.operation.operands; op != NULL; op = op->next) { if (expr_tree_contains_template_param_constant(op, (a_constant_ptr)NULL)) { found = TRUE; break; } /* if */ } /* for */ } /* if */ return found; } /* expr_tree_contains_template_param_constant */ a_source_correspondence_ptr nontype_templ_arg_constant_corresp( a_constant_ptr constant) /* Return the source correspondence entry of the non-type template argument constant, if it exists. */ { a_source_correspondence_ptr scp = NULL; if (constant->kind == (a_constant_repr_kind)ck_address) { /* An address constant. See if the object referenced is external. */ /* Get a pointer to the source correspondence for the entity. */ an_address_base_kind abk = constant->variant.address.kind; switch (abk) { case abk_routine: scp = &constant->variant.address.variant.routine->source_corresp; break; case abk_variable: scp = &constant->variant.address.variant.variable->source_corresp; break; case abk_constant: case abk_temporary: scp = &constant->variant.address.variant.constant->source_corresp; break; case abk_uuidof: if (constant->variant.address.variant.type != NULL) { scp = &constant->variant.address.variant.type->source_corresp; } /* if */ break; case abk_label: scp = &constant->variant.address.variant.label->source_corresp; break; case abk_typeid: case abk_param_ref: break; #if MICROSOFT_EXTENSIONS_ALLOWED case abk_cli_typeid: case abk_cli_array: break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ default: unexpected_condition_str2( "nontype_templ_arg_constant_corresp:", "bad address kind"); } /* switch */ } /* if */ return scp; } /* nontype_templ_arg_constant_corresp */ a_boolean nontype_templ_arg_constant_involves_invalid_linkage( a_constant_ptr constant) /* Return TRUE if the indicated constant is not valid as a nontype template argument because it references a non-external entity, e.g., a local variable. */ { a_boolean invalid = FALSE; if (constant_is(constant, ck_address)) { /* An address constant. See if the object referenced is external. */ /* Get a pointer to the source correspondence for the entity. */ an_address_base_kind abk = constant->variant.address.kind; a_source_correspondence *scp = NULL; scp = nontype_templ_arg_constant_corresp(constant); switch (abk) { case abk_routine: if (generalized_nontype_arguments) { /* From C++17 on (see N4268) the limitations for linkage on routines have been lifted. */ scp = NULL; } else if (microsoft_mode) { /* Microsoft compilers do not appear to check the linkage of a routine whose address is used as a template argument (e.g., the address of a static member function of a local class is accepted). */ scp = NULL; } /* if */ break; case abk_variable: case abk_constant: case abk_temporary: case abk_uuidof: case abk_label: break; case abk_typeid: /* A typeid operation in a nontype template argument (accepted in Microsoft mode) produces a reference to std::type_info object, which is always treated as an external entity. */ scp = NULL; break; #if MICROSOFT_EXTENSIONS_ALLOWED case abk_cli_typeid: case abk_cli_array: /* C++/CLI typeid and array constants are only valid in C++/CLI custom attribute argument expressions. */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case abk_param_ref: scp = NULL; invalid = TRUE; break; default: unexpected_condition_str2( "nontype_templ_arg_constant_involves_invalid_linkage:", "bad address kind"); } /* switch */ if (invalid) { /* We already have our answer. */ } else if (scp == NULL) { /* Nothing referenced. */ } else if (scp->is_class_member) { /* The entity is a class member. If the class is a local class, the entity is non-external. Otherwise, the class will be forced to be external by this reference. */ a_type_ptr class_type = scp_parent_class(scp); if (class_type->source_corresp.is_local_to_function) { invalid = TRUE; } else { /* Force the class to be external. */ set_force_external_linkage_flag(class_type); } /* if */ } else { /* Not a class member. Entities with no linkage or with internal linkage are sometimes invalid. */ if (scp->name_linkage == (a_name_linkage_kind)nlk_none) { /* This case is often invalid, but C++17 generalized nontype template arguments do permit any user variable with static storage duration. */ invalid = TRUE; if (generalized_nontype_arguments && (abk == (an_address_base_kind)abk_routine || (abk == (an_address_base_kind)abk_variable && constant->variant.address.variant.variable ->source_corresp.decl_position.seq != 0))) { invalid = FALSE; } /* if */ } else if (scp->name_linkage == (a_name_linkage_kind)nlk_internal) { /* C++11 enabled types with internal linkage as type template arguments and using entities with internal linkage as nontype template arguments falls under that same feature. */ if (!local_types_as_template_args_enabled) { invalid = TRUE; } /* if */ } /* if */ } /* if */ } /* if */ return invalid; } /* nontype_templ_arg_constant_involves_invalid_linkage */ a_boolean has_non_file_scope_ref(a_constant *cp) /* Return TRUE if the constant pointed to by cp includes a reference to something that's not in the file scope. If it does, the constant cannot be allocated at the file scope (it would contain a pointer down into a function scope). */ { a_boolean has_nfs_ref = FALSE; /* cp->type is always in the file scope. */ switch (cp->kind) { case ck_error: case ck_integer: #if FIXED_POINT_ALLOWED case ck_fixed_point: #endif /* FIXED_POINT_ALLOWED */ case ck_float: #if C99_IL_EXTENSIONS_SUPPORTED case ck_imaginary: case ck_complex: #endif /* C99_IL_EXTENSIONS_SUPPORTED */ case ck_designator: #if UPC_EXTENSIONS_ALLOWED case ck_upc_threads: case ck_upc_mythread: #endif /* UPC_EXTENSIONS_ALLOWED */ case ck_void: /* No references. */ break; case ck_string: /* String texts are always in the file scope. */ #if DO_IL_LOWERING && ASSIGN_STRING_LITERAL_SEQUENCE_NUMBERS /* However, string literals with assigned sequence numbers are specific to a function and must be allocated in the function scope memory region (we need to rewrite them in the context of the function). */ if (cp->variant.string.sequence_number != 0) { has_nfs_ref = TRUE; } /* if */ #endif /* DO_IL_LOWERING && ASSIGN_STRING_LITERAL_SEQUENCE_NUMBERS */ break; case ck_address: if (cp->variant.address.subobject_path != NULL && !in_file_scope(cp->variant.address.subobject_path)) { has_nfs_ref = TRUE; break; } /* if */ switch (cp->variant.address.kind) { case abk_routine: /* Routines are always in the file scope. */ break; case abk_variable: /* Usually one will only see static variables here, and they are allocated in the file scope. However, under control of the CAO_TREAT_LOCAL_VAR_ADDR_AS_CONSTANT flag, local variables might make it here. */ has_nfs_ref = !in_file_scope(cp->variant.address.variant.variable); break; case abk_constant: case abk_temporary: has_nfs_ref = !in_file_scope(cp->variant.address.variant.constant); break; case abk_uuidof: case abk_typeid: #if MICROSOFT_EXTENSIONS_ALLOWED case abk_cli_typeid: #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* The type pointed to must be in the file scope. */ break; #if MICROSOFT_EXTENSIONS_ALLOWED case abk_cli_array: /* C++/CLI array constants are always in the file scope. */ check_assertion(cp->expr != NULL && in_file_scope(cp->expr)); break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case abk_label: /* Labels are never in the file scope. */ has_nfs_ref = TRUE; break; case abk_param_ref: /* Parameters are always in file scope (and the IL representation currently does not contain pointers). */ break; default: unexpected_condition_str( "has_non_file_scope_ref: bad addr constant kind"); } /* switch */ break; case ck_aggregate: if (cp->variant.aggregate.first_constant != NULL) { has_nfs_ref = !in_file_scope(cp->variant.aggregate.first_constant); } /* if */ break; #if GNU_EXTENSIONS_ALLOWED case ck_label_difference: /* Labels are never in the file scope. */ has_nfs_ref = TRUE; break; #endif /* GNU_EXTENSIONS_ALLOWED */ case ck_ptr_to_member: /* The class type and the object (if any) pointed to must be in the file scope. */ break; #if DO_IL_LOWERING && GENERATE_EH_TABLES && !DO_FULL_PORTABLE_EH_LOWERING case ck_stack_offset: /* The variable pointed to must be in the function scope. */ break; #endif /* DO_IL_LOWERING && ... */ case ck_init_repeat: has_nfs_ref = !in_file_scope(cp->variant.init_repeat.constant); break; case ck_template_param: switch (cp->variant.template_param.kind) { case tpck_param: case tpck_member: case tpck_unknown_function: case tpck_template_ref: case tpck_destructor: break; case tpck_expression: has_nfs_ref = cp->variant.template_param.variant.expr != NULL && !in_file_scope(cp->variant.template_param.variant.expr); break; case tpck_address: case tpck_dependent_constant: has_nfs_ref = has_non_file_scope_ref(cp->variant.template_param .variant.constant); break; case tpck_concat_string_literals: { a_constant_ptr elem_cp = cp->variant.template_param .variant.string_literal_list; for (; elem_cp != NULL; elem_cp = elem_cp->next) { if (has_non_file_scope_ref(elem_cp)) { has_nfs_ref = TRUE; break; } /* if */ } /* for */ } break; case tpck_sizeof: case tpck_datasizeof: case tpck_alignof: case tpck_uuidof: case tpck_typeid: case tpck_noexcept: { an_expr_node_ptr expr = cp->variant.template_param.variant.templ_sizeof.expr; if (expr != NULL) has_nfs_ref = !in_file_scope(expr); } break; case tpck_integer_pack: has_nfs_ref = has_non_file_scope_ref(cp->variant.template_param.variant.bound); break; default: unexpected_condition_str( "has_non_file_scope_ref: bad template constant kind"); } /* switch */ break; case ck_dynamic_init: { a_dynamic_init_ptr dip = cp->variant.dynamic_init.ptr; switch (dip->kind) { case dik_none: case dik_zero: break; case dik_constant: has_nfs_ref = has_non_file_scope_ref(dip->variant.constant.ptr); break; case dik_expression: case dik_class_result_via_ctor: has_nfs_ref = !in_file_scope(dip->variant.expression); break; case dik_constructor: has_nfs_ref = !in_file_scope(dip->variant.constructor.args); break; case dik_bitwise_copy: has_nfs_ref = !in_file_scope(dip->variant.bitwise_copy.source); break; default: unexpected_condition_str( "has_non_file_scope_ref: bad dynamic init kind"); } /* switch */ } break; case ck_reflection: has_nfs_ref = cp->variant.reflection.entity.ptr != NULL && !in_file_scope(cp->variant.reflection.entity.ptr); break; default: unexpected_condition_str("has_non_file_scope_ref: bad constant kind"); } /* switch */ if (!has_nfs_ref && cp->expr != NULL && !in_file_scope(cp->expr)) { /* The constant recorded an expression that is not allocated in file scope. */ has_nfs_ref = TRUE; } /* if */ return has_nfs_ref; } /* has_non_file_scope_ref */ a_boolean constant_is_shareable(a_constant *cp) /* Return TRUE if the given constant could be shared. This is the guard code used by alloc_shareable_constant to decide on sharing, but given that, if it's applied to an allocated constant it can tell for sure if the constant is shared, because alloc_shareable_constant is the only way to share constants. It can also be applied to an unallocated constant, and will indicate whether alloc_shareable_constant would return a shareable constant. */ { a_boolean shareable; a_symbol_ptr assoc_symbol; /* Can't accurately tell whether a constant is shared without access to the symbol table, so don't allow this routine to be called in a back end. */ check_assertion(in_front_end); if ((assoc_symbol = symbol_for(cp)) != NULL) { /* For constants with a source correspondence indicated, there is a "master" copy available by going up the source correspondence link and back down again. This applies, for example, to enumeration constants. */ a_constant_ptr shared_cp; check_assertion(assoc_symbol->kind == (a_symbol_kind)sk_constant); shared_cp = assoc_symbol->variant.constant; if (assoc_symbol->is_template_param) { /* For template parameter symbols, only attempt to use the constant from the symbol if it points to an identical constant. */ shareable = identical_constants(cp, shared_cp); } else if (has_name(cp) && cp->kind != (a_constant_repr_kind)ck_template_param) { /* A named constant (enumerator or non-standard member constant) should only be shared if it is the definition, not simply a reference to it. */ shareable = cp->is_named_constant_definition; } else { /* The constant from the symbol can be reused. */ shareable = TRUE; } /* if */ #if CHECKING if (shareable && cp->implicit_cast != shared_cp->implicit_cast) { /* Someone did an implicit cast on the constant without clearing the source association. */ internal_error( "constant_is_shareable: implicitly-cast const has assoc_info"); } /* if */ #endif /* CHECKING */ } else if (cp->expr != NULL || cp->source_corresp.decl_position.seq != 0 || cp->is_generic_initializer) { /* Constants with backing expressions or associated with specific source positions should not be shared. Similarly, a constant that is used as a generic initializer is context-specific and should not be shared. */ shareable = FALSE; } else { switch (cp->kind) { case ck_address: if (cp->variant.address.subobject_path != NULL) { /* Do not share address constants with a subobject path. */ shareable = FALSE; } else if (cp->variant.address.kind == (an_address_base_kind)abk_uuidof && cp->variant.address.variant.type != NULL) { /* Do not share __uuidof address constants with an associated type since a back end might be interested in the specific type that was recorded. */ shareable = FALSE; } else { shareable = TRUE; } /* if */ break; case ck_aggregate: case ck_designator: /* Don't share aggregate constants (they come up for compound literals used to initialize static variables in gcc mode, when recording constant expressions). Similarly, do not share designators. */ shareable = FALSE; break; case ck_string: /* Don't share string literals if told not to. */ shareable = string_literals_shared; break; case ck_ptr_to_member: /* Pointer to member constants with an attached name reference should not be shared. */ shareable = (cp->variant.ptr_to_member.name_reference == NULL); break; case ck_template_param: /* Template param constants should not be made part of the IL tree proper, unless prototype instantiations are recorded in the IL. In the latter case, the constant may need to refer to a local expression, which prevents sharing. Those with assoc_info non-NULL were handled above. For others, make a new copy every time. */ shareable = FALSE; break; default: /* Other cases are shareable. */ shareable = TRUE; } /* switch */ } /* if */ return shareable; } /* constant_is_shareable */ a_constant_ptr alloc_shareable_constant(a_constant *cp) /* Find or allocate a constant with the indicated value. This constant is shareable (it can be reused by others), and therefore the caller cannot modify it (change its value or type, set its source correspondence, or put it on a list of constants). */ { a_constant_ptr scp, prev_scp; a_symbol_ptr assoc_symbol; a_hash_value hash_value; a_boolean alloc_in_function_scope; a_constant_ptr *list_ptr; #if DEBUG num_searches_for_shareable_constants++; #endif /* DEBUG */ #if DO_IL_LOWERING && ASSIGN_STRING_LITERAL_SEQUENCE_NUMBERS if (cp->kind == (a_constant_repr_kind)ck_string && cp->variant.string.sequence_number != 0 && cp->expr != NULL) { /* If we're sharing string literals and assigning string literal sequence numbers for the IA-64 ABI, it is imperative that equal string literals with a sequence number be shared (because they may be stored in comdat storage). To ensure that that is possible, we therefore clear the backing expression (a backing expression would disable sharing). */ cp->expr = NULL; } /* if */ #endif /* DO_IL_LOWERING && ASSIGN_STRING_LITERAL_SEQUENCE_NUMBERS */ if (!in_front_end) { /* If we're not in the front end, the shareable constants table is not available, nor is the assoc_info pointer. */ scp = alloc_unshared_constant(cp); } else if (!constant_is_shareable(cp)) { /* The constant should be unshared. */ scp = alloc_unshared_constant(cp); } else if ((assoc_symbol = symbol_for(cp)) != NULL) { /* For constants with a source correspondence indicated, find the "master" copy by going up the source correspondence link and back down again. Note that constant_is_shareable ruled out some of these cases as being unshareable. */ check_assertion(assoc_symbol->kind == (a_symbol_kind)sk_constant); scp = assoc_symbol->variant.constant; } else { /* The constant is shareable. */ /* If the current IL region is not the file scope region (i.e., it's a function scope), and the constant has one or more references to things that are in the function scope, the constant cannot be allocated and shared at the file scope. */ alloc_in_function_scope = (curr_il_region_number != file_scope_region_number) && has_non_file_scope_ref(cp); if (alloc_in_function_scope) { /* The constant cannot be shared at the file scope. Look for a copy on the list of shared constants for the current function. Bear in mind that the only constants likely to be on this list are those that represent the address of a local variable, so the list is going to be fairly short. In certain cases (e.g., when using the IA-64 ABI) the list also contains string literals. */ if (depth_innermost_function_scope == NO_SCOPE_DEPTH) { /* No function on the scope stack (we're probably in IL lowering). There's no list of shareable constants. */ list_ptr = NULL; } else { /* Look for the constant in the hash table of constants shared at the function level. */ a_function_shareable_constants_table_ptr fsctp; fsctp = scope_stack[depth_innermost_function_scope]. shareable_constants_table; if (fsctp == NULL) { /* This is the first use of the hash table for this function. Allocate it now. */ fsctp = alloc_function_shareable_constants_table(); scope_stack[depth_innermost_function_scope] .shareable_constants_table = fsctp; } /* if */ /* Determine the bucket of the hash table to use. */ hash_value = hash_constant(cp) % SIZE_FUNCTION_SHAREABLE_CONSTANTS_TABLE; list_ptr = &fsctp->table[hash_value]; } /* if */ } else { /* The constant can be shared at the file scope. */ /* Look for a copy of the constant value in the shareable_constants_table. */ /* Determine the bucket of the hash table to use. */ hash_value = hash_constant(cp) % SIZE_SHAREABLE_CONSTANTS_TABLE; list_ptr = &shareable_constants_table[hash_value]; } /* if */ if (!string_literals_shared && (cp->kind == (a_constant_repr_kind)ck_string || (cp->kind == (a_constant_repr_kind)ck_address && cp->variant.address.kind == (an_address_base_kind)abk_constant && cp->variant.address.variant.constant->kind == (a_constant_repr_kind)ck_string))) { /* We're not sharing string literals, and the constant is a string or the address of a string. There's no point in putting such an entry on the shared list since no one else will need the same constant anyway. */ list_ptr = NULL; /* No list. */ } /* if */ if (list_ptr != NULL) { /* Search the entries in the list, if any. */ for (prev_scp = NULL, scp = *list_ptr; scp != NULL; prev_scp = scp, scp = scp->next) { #if DEBUG num_compares_for_shareable_constants++; #endif /* DEBUG */ /* Compare the constant in the list with the desired constant. */ if (identical_constants(scp, cp)) { /* The constants are the same, so we have found a reusable constant. */ /* Remove the constant from the list. It will be re-added at the front of the list below. This is so that common constants stay near the front of the bucket list, to speed lookup. */ if (prev_scp == NULL) { *list_ptr = scp->next; } else { prev_scp->next = scp->next; } /* if */ break; } /* if */ } /* for */ } else { /* Not shareable. */ scp = NULL; } /* if */ if (scp == NULL) { /* No identical constant exists in the table, so create one. */ if (alloc_in_function_scope) { scp = alloc_constant(cp->kind); } else { scp = fs_constant(cp->kind); } /* if */ copy_constant(cp, scp); fix_memory_region_problems_in_copied_constant(scp); #if DEBUG if (list_ptr != NULL) { if (alloc_in_function_scope) { num_func_shareable_constants++; } else { num_shareable_constants++; if (*list_ptr == NULL) { num_used_shareable_constant_buckets++; } /* if */ } /* if */ } /* if */ #endif /* DEBUG */ } /* if */ if (list_ptr != NULL) { /* Add the shareable constant to the front of the proper list. */ scp->next = *list_ptr; *list_ptr = scp; } /* if */ } /* if */ return scp; } /* alloc_shareable_constant */ a_constant_ptr shareable_fs_string_constant(a_const_char *str) /* Return a shareable version of the given string allocated in file scope. This ignores the "string_literals_shared" flag (the given string is not the result of a string literal, but, e.g., of a call to a built-in function). */ { a_hash_value hash_value; a_constant_ptr cp = local_constant(), scp, prev_scp, *list_ptr; sizeof_t length = strlen(str)+1; clear_constant(cp, (a_constant_repr_kind)ck_string); cp->type = string_type((a_targ_size_t)length); cp->variant.string.length = (a_targ_size_t)length; cp->variant.string.value = str; hash_value = hash_constant(cp) % SIZE_SHAREABLE_CONSTANTS_TABLE; list_ptr = &shareable_constants_table[hash_value]; /* Search the entries in the list, if any. */ for (prev_scp = NULL, scp = *list_ptr; scp != NULL; prev_scp = scp, scp = scp->next) { #if DEBUG num_compares_for_shareable_constants++; #endif /* DEBUG */ /* Compare the constant in the list with the desired constant. */ if (identical_constants(scp, cp)) { /* The constants are the same: We have found a reusable constant. */ /* Remove the constant from the list. It will be re-added at the front of the list below. This is so that common constants stay near the front of the bucket list, to speed lookup. */ if (prev_scp == NULL) { *list_ptr = scp->next; } else { prev_scp->next = scp->next; } /* if */ break; } /* if */ } /* for */ if (scp == NULL) { /* No identical constant exists in the table, so create one. */ scp = fs_constant((a_constant_repr_kind)ck_string); copy_constant(cp, scp); scp->variant.string.value = alloc_text_of_string_literal((sizeof_t)length); (void)strcpy((char*)scp->variant.string.value, cp->variant.string.value); fix_memory_region_problems_in_copied_constant(scp); #if DEBUG check_assertion(list_ptr != NULL); num_shareable_constants++; if (*list_ptr == NULL) { num_used_shareable_constant_buckets++; } /* if */ #endif /* DEBUG */ } /* if */ check_assertion(list_ptr != NULL); /* Add the shareable constant to the front of the proper list. */ scp->next = *list_ptr; *list_ptr = scp; release_local_constant(&cp); return scp; } /* shareable_fs_string_constant */ void add_backing_expression_for_named_constant(a_constant *cp) /* cp is the result of scanning a constant expression, and it is about to be made into the value of a named constant (e.g., an enumerator). Give it a backing expression if putting a name into the constant entry would destroy information about the fact that the expression is itself a reference to a named constant. It is presumed that the constant is unshared. */ { if (cp->expr == NULL && has_name(cp)) { cp->expr = alloc_node_for_constant(cp); } /* if */ } /* add_backing_expression_for_named_constant */ void empty_shareable_constants_table(void) /* Empty out the file-scope shareable constants table and liberate the constants therein by clearing their "next" fields. This is called at the end of compilation, when the shareable constants table is no longer needed. Note that the information in the table is not needed to produce the debug space summary for the shareable constants table -- there are separate variables that are set already. */ { a_constant_ptr scp, next_scp; a_hash_value hash_value; /* For each bucket of the hash table ... */ for (hash_value = 0; hash_value < SIZE_SHAREABLE_CONSTANTS_TABLE; hash_value++) { /* For each entry on the linked list for that bucket ... */ for (scp = shareable_constants_table[hash_value]; scp != NULL; scp = next_scp) { next_scp = scp->next; scp->next = NULL; } /* for */ shareable_constants_table[hash_value] = NULL; } /* for */ } /* empty_shareable_constants_table */ void empty_func_shareable_constants_table(void) /* Empty out the function-scope shareable constants table, and liberate the constants therein by clearing their "next" fields. */ { a_constant_ptr scp, next_scp; a_function_shareable_constants_table_ptr fsctp; int bucket; check_assertion(depth_innermost_function_scope != NO_SCOPE_DEPTH); fsctp = scope_stack[depth_innermost_function_scope] .shareable_constants_table; if (fsctp != NULL) { for (bucket = 0; bucket < SIZE_FUNCTION_SHAREABLE_CONSTANTS_TABLE; ++bucket) { scp = fsctp->table[bucket]; for (; scp != NULL; scp = next_scp) { next_scp = scp->next; scp->next = NULL; } /* for */ /* The table should be cleared before it is freed. */ fsctp->table[bucket] = NULL; } /* for */ free_function_shareable_constants_table(fsctp); scope_stack[depth_innermost_function_scope] .shareable_constants_table = NULL; } /* if */ } /* empty_func_shareable_constants_table */ a_namespace_ptr namespace_enclosing_class(a_type_ptr tp) /* Return the namespace enclosing a possibly nested class tp. Return NULL if the class is local to a function or if it is enclosed by the file scope. */ { a_scope_ptr parent_scope = class_type_supp(tp)->assoc_scope; a_namespace_ptr result = NULL; while (parent_scope != NULL && parent_scope->kind == (a_scope_kind)sck_class_struct_union) { parent_scope = parent_scope->parent; } /* while */ if (parent_scope != NULL && parent_scope->kind == (a_scope_kind)sck_namespace) { result = parent_scope->variant.assoc_namespace; } else { result = NULL; } /* if */ return result; } /* namespace_enclosing_class */ void add_scope_to_class_type(a_type_ptr type) /* The given class type does not yet have an associated scope. Add one. */ { a_scope_ptr scope; a_memory_region_number region_to_switch_back_to = NULL_region_number; a_scope_number number = NO_SCOPE_NUMBER; a_symbol_ptr sym; switch_to_file_scope_region(®ion_to_switch_back_to); sym = symbol_for(type); if (sym != NULL) { /* For proxy classes, a scope number will have been assigned but no scope allocated. If a scope number has been assigned, use that for the scope that is now being created. There will not be a symbol when this routine is used for type created by lowering. */ number = symbol_for(type)->variant.class_struct_union.extra_info-> member_decl_scope; } /* if */ if (number == NO_SCOPE_NUMBER) number = take_next_scope_number(); scope = alloc_scope((a_scope_kind)sck_class_struct_union, number, (a_routine_ptr)NULL); switch_back_to_original_region(region_to_switch_back_to); scope->parent = type->source_corresp.parent_scope; class_type_supp(type)->assoc_scope = scope; scope->variant.assoc_type = type; } /* add_scope_to_class_type */ a_scope_ptr ensure_il_scope_exists(a_scope_stack_entry_ptr ssep) /* Make sure that the scope stack entry pointed to by ssep points to an IL scope. If it does not and the current scope stack entry is for a block scope, create the scope entry and return it. (Note that a NULL pointer is returned only for scopes that might appear in exceptional cases; the caller is responsible for sorting that out.) */ { a_scope_ptr sp = ssep->il_scope; a_memory_region_number region_to_switch_back_to = NO_MEMORY_REGION_NUMBER; if (sp == NULL) { /* There is no IL scope. */ if (ssep->kind == (a_scope_kind)sck_block || ssep->kind == (a_scope_kind)sck_condition) { a_scope_stack_entry_ptr parent_ssep = previous_scope_of(ssep); a_boolean il_region_changed; /* Create the IL scope in a block scope. */ (void)ensure_il_scope_exists(parent_ssep); if (curr_il_region_number == file_scope_region_number) { region_to_switch_back_to = curr_il_region_number; switch_il_region(ssep->il_memory_region); il_region_changed = TRUE; } else { ssep->il_memory_region = curr_il_region_number; il_region_changed = FALSE; } /* if */ ssep->il_scope = sp = alloc_scope(ssep->kind, ssep->number, (a_routine_ptr)NULL); if (il_region_changed) { switch_il_region(region_to_switch_back_to); } /* if */ /* Set the parent scope. */ ssep->il_scope->parent = parent_ssep->il_scope; /* Add it to the scopes list for the scope enclosing the scope indicated by ssep. If the lexical parent scope is in file-scope memory but this scope is in a function memory region (possible when GNU statement expressions appear in unevaluated contexts in a local class), add it to the enclosing function's scope list instead. That keeps function-scope entries reachable without creating a file-scope to function-scope pointer. */ if (is_local_scope_kind(parent_ssep->kind)) { add_to_scopes_list(sp, ssep-1); } else if (ssep->il_memory_region != file_scope_region_number && ssep->depth_innermost_function_scope != NO_SCOPE_DEPTH) { a_scope_stack_entry_ptr func_ssep = &scope_stack[ssep->depth_innermost_function_scope]; ssep->il_scope->parent = func_ssep->il_scope; add_to_scopes_list(sp, func_ssep); } /* if */ if (!C_mode()) { /* An object lifetime will have been created for this scope in push scope. Now that the scope entry exists, bind the two entries to one another. */ bind_object_lifetime(ssep->curr_scope_object_lifetime, (an_il_entry_kind)iek_scope, (char *)sp); } /* if */ } else if (ssep->kind == (a_scope_kind)sck_func_prototype) { a_type_ptr routine_type; /* A prototype scope must be allocated. It is always allocated in the file scope memory region because it is pointed to from the routine type supplement, which is always at file scope. */ region_to_switch_back_to = curr_il_region_number; switch_il_region(file_scope_region_number); sp = alloc_scope((a_scope_kind)sck_func_prototype, ssep->number, (a_routine_ptr)NULL); switch_il_region(region_to_switch_back_to); ssep->il_scope = sp; /* Call add_to_scopes_list only if this is a function prototype nested within another function prototype. A function prototype scope that is not nested is just pointed to from the routine type, not from the function scope entry (which will not exist if the function is not defined). As a consequence, the parent scope of a function prototype scope is NULL except if it is a nested prototype scope. */ if ((ssep-1)->kind == (a_scope_kind)sck_func_prototype) { add_to_scopes_list(sp, ssep-1); sp->parent = ensure_il_scope_exists(ssep-1); } else { sp->parent = NULL; } /* if */ /* Link the routine type and the prototype scope entry to each other. */ routine_type = ssep->assoc_type; if (routine_type != NULL) { routine_type = skip_typerefs(routine_type); routine_type->variant.routine.extra_info->prototype_scope = sp; sp->variant.assoc_type = routine_type; } else { /* This can happen if this is the function prototype scope created for a requires-expression. */ check_assertion_str(ssep->decl_parse_state != NULL && ssep->decl_parse_state->for_requires_expr_params, "ensure_il_scope_exists: routine_type is NULL"); } /* if */ } /* if */ if (sp != NULL) { /* Set the scope-stack-entry depth if not already set. */ if (sp->depth_in_scope_stack == NO_SCOPE_DEPTH) { sp->depth_in_scope_stack = scope_depth_of(ssep); } /* if */ } else { check_assertion_str(ssep->kind == (a_scope_kind)sck_pragma, "ensure_il_scope_exists: NULL IL scope"); } /* if */ } /* if */ return sp; } /* ensure_il_scope_exists */ void add_to_constants_list(a_constant_ptr con_ptr, a_boolean at_file_scope) /* Add the given constant to the constants list for the file scope or the current scope. This is used for member constants. */ { a_scope_stack_entry_ptr ssep; a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; /* Get pointer to current or file scope entry. */ ssep = &scope_stack[at_file_scope ? DEPTH_OF_FILE_SCOPE : decl_scope_level]; /* Create the IL scope if necessary (for block scopes). */ sp = ensure_il_scope_exists(ssep); pointers_block = assoc_pointers_block_of(ssep); check_assertion_str(sp != NULL, "add_to_constants_list: NULL IL scope"); if (sp->constants == NULL) { sp->constants = con_ptr; } else { pointers_block->last_constant->next = con_ptr; } /* if */ pointers_block->last_constant = con_ptr; con_ptr->next = NULL; if (!has_parent_scope(con_ptr)) { /* Set the parent scope the first time the constant is added to a scope. IL lowering may add it to a different scope, but in that case we want to retain the original scope as the parent scope. */ set_parent_scope(&con_ptr->source_corresp, iek_constant, sp); } /* if */ } /* add_to_constants_list */ void set_integer_constant(a_constant *cp, a_host_large_integer value, an_integer_kind kind) /* Set the constant entry *cp to the integer constant given by value. Its integer kind is as given by kind. Note that the kind is not restricted to be a signed kind. */ { clear_constant(cp, ck_integer); cp->type = integer_type(kind); set_integer_value(&cp->variant.integer_value, value); } /* set_integer_constant */ void set_unsigned_integer_constant(a_constant *cp, a_host_large_unsigned value, an_integer_kind kind) /* Set the constant entry *cp to the integer constant given by value. Its integer kind is as given by kind. Note that the kind is not restricted to be an unsigned kind. */ { clear_constant(cp, ck_integer); cp->type = integer_type(kind); set_unsigned_integer_value(&cp->variant.integer_value, value); } /* set_unsigned_integer_constant */ #if !INTEGER_VALUE_REPR_IS_A_HOST_INTEGER void set_integer_constant(a_constant *cp, const an_integer_value &value, an_integer_kind kind) /* Set the constant entry *cp to the integer constant given by value. Its integer kind is as given by kind. Note that the kind is not restricted to be a signed kind. */ { clear_constant(cp, (a_constant_repr_kind)ck_integer); cp->type = integer_type(kind); set_integer_value(&cp->variant.integer_value, value); } /* set_integer_constant */ void set_unsigned_integer_constant(a_constant *cp, const an_integer_value &value, an_integer_kind kind) /* Set the constant entry *cp to the integer constant given by value. Its integer kind is as given by kind. Note that the kind is not restricted to be an unsigned kind. */ { clear_constant(cp, (a_constant_repr_kind)ck_integer); cp->type = integer_type(kind); set_unsigned_integer_value(&cp->variant.integer_value, value); } /* set_unsigned_integer_constant */ #endif /* !INTEGER_VALUE_REPR_IS_A_HOST_INTEGER */ void make_zero_of_proper_type(a_type_ptr desired_type, a_constant *zero_constant) /* Make a zero constant of type desired_type (a scalar type) and put it in *zero_constant. No IL allocation is done. This routine is also handy for making NULL pointer constants. */ { a_boolean did_not_fold; /* Make an integer zero and convert it to the desired type. */ set_integer_constant(zero_constant, (a_host_large_integer)0, (an_integer_kind)ik_int); type_change_constant(zero_constant, desired_type, /*is_implicit_cast=*/TRUE, /*maintain_expression=*/FALSE, &did_not_fold, &error_position); } /* make_zero_of_proper_type */ an_expr_node_ptr make_zero_expr(a_type_ptr tp) /* Return a node representing a "zero" constant of the given type. */ { a_constant_ptr zero = local_constant(); an_expr_node_ptr result; make_zero_of_proper_type(tp, zero); result = alloc_node_for_constant(zero); release_local_constant(&zero); return result; } /* make_zero_expr */ void make_one_of_proper_type(a_type_ptr desired_type, a_constant *one_constant) /* Make a "one" constant of type desired_type (a scalar type) and put it in *one_constant. No IL allocation is done. This routine is also handy for making "true" values. */ { a_boolean did_not_fold; /* Make an integer one and convert it to the desired type. */ set_integer_constant(one_constant, (a_host_large_integer)1, (an_integer_kind)ik_int); type_change_constant(one_constant, desired_type, /*is_implicit_cast=*/TRUE, /*maintain_expression=*/FALSE, &did_not_fold, &error_position); } /* make_one_of_proper_type */ an_expr_node_ptr make_one_expr(a_type_ptr tp) /* Return a node representing a "one" constant of the given type. */ { a_constant_ptr one = local_constant(); an_expr_node_ptr result; make_one_of_proper_type(tp, one); result = alloc_node_for_constant(one); release_local_constant(&one); return result; } /* make_one_expr */ void make_bool_constant_value(a_boolean val, a_constant_ptr con) /* Set the given constant to the given boolean value and to type bool. */ { set_integer_constant(con, (a_host_large_integer)val, BOOL_INT_KIND); con->type = bool_type(); } /* make_bool_constant_value */ a_boolean make_value_initialized_constant(a_type_ptr type, a_constant *con) /* If possible, make a constant of the specified type in *con and return TRUE if the constant was successfully created. If type is a reference type, no constant is created. Otherwise, the type can be a scalar, in which case this routine is a wrapper for make_zero_of_proper_type, or an aggregate, in which case the resulting constant is an empty aggregate. */ { a_boolean return_value = FALSE; a_class_symbol_supplement_ptr cssp; a_type_ptr utp = skip_typerefs(type); if (is_any_reference_type(utp)) { /* Cannot create a value-initialized reference type. This can result from upstream error recovery. */ } else if (type_is(utp, tk_reflection)) { clear_constant(con, ck_reflection); con->type = type; con->variant.reflection.entity.kind = iek_none; con->variant.reflection.entity.ptr = (char*)NULL; return_value = TRUE; } else if (is_scalar_type(utp)) { make_zero_of_proper_type(type, con); return_value = TRUE; } else if (is_error_type(utp)) { set_error_constant(con); return_value = TRUE; } else if (is_aggregate_type(utp) || is_vector_type(utp) || (is_immediate_class_type(utp) && (cssp = class_symbol_supp(symbol_for(utp)), has_any_trivial_default_constructor(cssp)))) { return_value = TRUE; clear_constant(con, ck_aggregate); con->type = type; if (is_immediate_class_type(utp)) { a_base_class_ptr bcp; for (bcp = base_classes_of(utp); bcp != NULL; bcp = bcp->next) { if (bcp->direct) { /* Add an empty aggregate for each direct base class. */ a_constant_ptr base_con = alloc_constant(ck_aggregate); if (!make_value_initialized_constant(bcp->type, base_con)) { return_value = FALSE; break; } /* if */ base_con->constant_for_base_class = TRUE; base_con->constant_for_base_class_from_constexpr_folding = TRUE; add_constant_to_aggregate(base_con, con, bcp, (a_field_ptr)NULL); } /* if */ } /* for */ } /* if */ if (!is_immediate_class_type(utp) || next_initializable_field(fields_of(utp)) != NULL) { con->partial_aggr_value = TRUE; con->is_partially_initialized = TRUE; } /* if */ } /* if */ return return_value; } /* make_value_initialized_constant */ #if MICROSOFT_EXTENSIONS_ALLOWED void make_uuidof_constant(a_type_ptr uuidof_type, a_constant_ptr uuidof_con) /* Make a constant for __uuidof(uuidof_type) in *uuidof_con. This is a Microsoft extension. */ { a_type_ptr const_guid_type = make_qualified_type( type_of_guid, (a_type_qualifier_set)TQ_CONST); clear_constant(uuidof_con, (a_constant_repr_kind)ck_address); uuidof_con->variant.address.kind = (an_address_base_kind)abk_uuidof; uuidof_con->variant.address.variant.type = uuidof_type; uuidof_con->type = make_pointer_type(const_guid_type); } /* make_uuidof_constant */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ a_type_ptr typeid_constant_type(ARG_UNUSED a_boolean is_cli_typeid) /* Return the type for a "typeid constant"; i.e., a constant representing the address of a standard C++ typeid(x) expression, or, if is_cli_typeid is TRUE, a handle to a T::typeid expression. */ { a_type_ptr constant_type = NULL; #if MICROSOFT_EXTENSIONS_ALLOWED if (is_cli_typeid) { /* C++/CLI T::typeid. The result type is System::Type^. */ a_type_ptr system_type; check_assertion(cli_or_cx_enabled); system_type = type_symbol_type(cli_symbol_from_kind(csk_system_type)); constant_type = make_handle_type(system_type); } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here. */ { /* Standard C++ typeid: the result type is const type_info. */ a_type_ptr const_type_info = make_qualified_type( type_of_type_info, (a_type_qualifier_set)TQ_CONST); constant_type = make_pointer_type(const_type_info); } /* if */ return constant_type; } /* typeid_constant_type */ void make_typeid_constant(a_type_ptr typeid_type, a_boolean is_cli_typeid, a_constant_ptr typeid_con) /* Set *typeid_con to a constant representing the address of typeid(typeid_type) (Standard C++) or, if is_cli_typeid is TRUE, a handle to a System::Type object (in C++/CLI mode). */ { clear_constant(typeid_con, (a_constant_repr_kind)ck_address); #if MICROSOFT_EXTENSIONS_ALLOWED if (is_cli_typeid) { check_assertion(cli_or_cx_enabled); typeid_con->variant.address.kind = (an_address_base_kind)abk_cli_typeid; } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here. */ { typeid_con->variant.address.kind = (an_address_base_kind)abk_typeid; } typeid_con->variant.address.variant.type = typeid_type; typeid_con->type = typeid_constant_type(is_cli_typeid); } /* make_typeid_constant */ #if MICROSOFT_EXTENSIONS_ALLOWED void make_cli_array_constant(an_expr_node_ptr gcnew_expr, a_constant_ptr array_con) /* Set *array_con to a constant representing a handle to a C++/CLI System::Array object created by gcnew_expr. This is used in the context of C++/CLI custom attribute argument expressions. */ { check_assertion(cli_or_cx_enabled && gcnew_expr != NULL && array_con != NULL && gcnew_expr->kind == (an_expr_node_kind)enk_gcnew && gcnew_expr->type != NULL && is_handle_to_cli_array_type(gcnew_expr->type)); clear_constant(array_con, (a_constant_repr_kind)ck_address); array_con->variant.address.kind = (an_address_base_kind)abk_cli_array; array_con->type = gcnew_expr->type; array_con->expr = gcnew_expr; } /* make_cli_array_constant */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_boolean is_enum_constant(a_constant_ptr con) /* Return TRUE if the indicated constant is an enum constant. Note that if you want to know if the enum constant is one that appears on the constant list of an enum type, you must also test for it having a name; there are copies of enum constants in initializer lists, and constants formed by casting integral constants to an enum type, which do not correspond to any enum constant. Also, to test whether the specific IL entry is one appearing on the list pointed to by the corresponding enumeration type (possibly via a scope entry in the case of scoped enum types), test the con->is_named_constant_definition flag. */ { a_boolean is_enum = FALSE; if (constant_is(con, ck_integer)) { /* The constant has an integral representation. */ a_type_ptr con_type = skip_lexical_typerefs(con->type); if (con_type->kind == (a_type_kind)tk_integer) { /* The constant has an integral or enum type. */ /* In C, enumerators have "int" type (but an affiliated type that is the enumeration); in C++, enumerators have the enum type. */ if (il_header.source_language == sl_C ? (!con_type->variant.integer.enum_type && con_type->variant.integer.enum_info.affiliated_type != NULL) : con_type->variant.integer.enum_type) { is_enum = TRUE; } /* if */ } /* if */ } else if (con->is_named_constant_definition && constant_is(con, ck_template_param) && !con->source_corresp.is_class_member) { /* Some enumerator constants are ck_template_param entries. */ is_enum = TRUE; } /* if */ return is_enum; } /* is_enum_constant */ a_boolean is_ordinary_string_constant(a_constant_ptr constant) /* Return TRUE if the indicated constant is a wide string constant (L"abc"). */ { return (constant->kind == (a_constant_repr_kind)ck_string && is_normal_character_kind(constant->character_kind)); } /* is_ordinary_string_constant */ a_boolean is_wide_string_constant(a_constant_ptr constant) /* Return TRUE if the indicated constant is a wide string constant (L"abc"). */ { return (constant->kind == (a_constant_repr_kind)ck_string && constant->character_kind == (a_character_kind)chk_wchar_t); } /* is_wide_string_constant */ #if !STANDALONE_UTILITY_PROGRAM void set_arg_transfer_method_flag(a_param_type_ptr ptp, a_source_position *err_pos) /* Set the flag in the indicated parameter type entry to indicate whether or not the parameter should be passed using a copy constructor. Also issue a diagnostic if the parameter type is an abstract class. */ { a_type_ptr param_type; /* Do not set the flag in C mode. Also, once the flag is set to TRUE it can never be reset. */ if (C_dialect == C_dialect_cplusplus && !ptp->passed_via_copy_constructor) { param_type = ptp->type; param_type = skip_typerefs(param_type); if (is_immediate_class_type(param_type)) { /* The parameter is a class passed by value. See if the class has a "real" copy constructor. */ if (is_incomplete_type(param_type)) { /* Delay setting the flag till the class is defined -- add it to the fixup list. */ add_to_dependent_type_fixup_list(param_type, (a_dependent_type_fixup_kind) dtfk_arg_transfer_method, (char *)ptp, iek_param_type, err_pos); } else { a_class_symbol_supplement_ptr cssp = symbol_supplement_for_class(param_type); if (!cssp->construction_by_bitwise_copy_allowed || (!any_cfront_mode() && has_deleted_or_nontrivial_destructor(cssp))) { /* The class has a "real" copy constructor, or it has a destructor, so a copy of an object of this class type must be made when it is passed as an argument. */ ptp->passed_via_copy_constructor = TRUE; /* If the parameter type is an abstract class, issue an error. Note that construction_by_bitwise_copy_allowed will never be TRUE for abstract classes. Also note that this logic assumes that passed_via_copy_constructor will never be set elsewhere. */ if (param_type->variant.class_struct_union.abstract) { an_error_severity sev = es_error; #if GNU_EXTENSIONS_ALLOWED || MICROSOFT_EXTENSIONS_ALLOWED if ((gpp_mode || microsoft_mode) && (is_nonspecialized_instantiation_context() || depth_template_declaration_scope != NO_SCOPE_DEPTH)) { /* Microsoft and GNU C++ accept abstract class parameter in non-top-level function types created during declarations and instantiations of function templates. */ a_scope_depth level = 0; while (scope_stack[depth_scope_stack-level].kind == (a_scope_kind)sck_func_prototype) { level += 1; } /* while */ /* If the level is larger than 1, this is a non-top-level parameter type. */ if (level > 1) sev = es_warning; } /* if */ #endif /* GNU_EXTENSIONS_ALLOWED || MICROSOFT_EXTENSIONS_ALLOWED */ if (err_pos->seq == 0 || relaxed_abstract_checking) { /* A null error position indicates a parameter type for which there is no corresponding source position -- e.g., a type is being copied for some reason. Issue no diagnostic in such cases or when relaxed abstract checking (P0929R2) is in effect (in the latter case, the parameter type will be checked when the function is defined or called). */ } else { abstract_class_diagnostic(sev, ec_abstract_class_param_type, param_type, err_pos); } /* if */ } /* if */ } /* if */ } /* if */ } /* if */ } /* if */ } /* set_arg_transfer_method_flag */ a_param_type_ptr make_param_type(a_type_ptr tp, a_source_position *decl_pos) /* Allocate a new param type entry, setting its "type" to tp and determining the value for its "passed_via_copy_constructor" flag, and return a pointer to it. *decl_position is used for issuing diagnostics. */ { a_param_type_ptr ptp; ptp = alloc_param_type(tp); set_arg_transfer_method_flag(ptp, decl_pos); return ptp; } /* make_param_type */ void update_param_top_level_qualifiers(a_param_type_ptr ptp) /* Given a parameter (ptp), update the top-level qualifiers of the parameter as needed. */ { if (remove_qualifiers_from_param_types) { /* Strip off top-level type qualifiers. They are not part of the type signature of a C++ function -- see [dcl.fct]p5. We apply this rule even for old-style parameter declarations. */ a_type_qualifier_set qualifiers; check_assertion(!C_mode()); qualifiers = get_type_qualifiers(ptp->type); if (qualifiers != TQ_NONE) { ptp->type = make_unqualified_type(ptp->type); if ((qualifiers & TQ_RESTRICT) && keep_restrict_in_signatures) { ptp->type = make_qualified_type(ptp->type, TQ_RESTRICT); } /* if */ /* Record the top-level type qualifiers that were declared for this parameter and then removed. */ copy_qualifiers(qualifiers, ptp->qualifiers); } /* if */ } /* if */ } /* update_param_top_level_qualifiers */ a_type_ptr make_routine_type(a_type_ptr return_type, /* Defaulted: */ a_type_ptr param1_type, a_type_ptr param2_type, a_type_ptr param3_type, a_type_ptr param4_type, a_type_ptr param5_type, a_type_ptr param6_type, a_type_ptr param7_type) /* Create a routine type with the given return type (which cannot be NULL) and the given parameter types (which are NULL by default). */ { a_type_ptr rout_type = alloc_type((a_type_kind)tk_routine); a_routine_type_supplement_ptr extra_info = rout_type->variant.routine.extra_info; a_source_position *np = &null_source_position; /* Return type. */ rout_type->variant.routine.return_type = return_type; /* Set the first parameter, if any. */ if (param1_type != NULL) { extra_info->param_type_list = make_param_type(param1_type, np); extra_info->param_type_list->param_num = 1; /* Set the second parameter, if any. */ if (param2_type != NULL) { a_param_type_ptr ptp = extra_info->param_type_list; ptp->next = make_param_type(param2_type, np); ptp->next->param_num = 2; /* Set the third parameter, if any. */ if (param3_type != NULL) { ptp = ptp->next; ptp->next = make_param_type(param3_type, np); ptp->next->param_num = 3; /* Set the fourth parameter, if any. */ if (param4_type != NULL) { ptp = ptp->next; ptp->next = make_param_type(param4_type, np); ptp->next->param_num = 4; /* Set the fifth parameter, if any. */ if (param5_type != NULL) { ptp = ptp->next; ptp->next = make_param_type(param5_type, np); ptp->next->param_num = 5; /* Set the sixth parameter, if any. */ if (param6_type != NULL) { ptp = ptp->next; ptp->next = make_param_type(param6_type, np); ptp->next->param_num = 6; /* Set the seventh parameter, if any. */ if (param7_type != NULL) { ptp = ptp->next; ptp->next = make_param_type(param7_type, np); ptp->next->param_num = 7; } /* if */ } /* if */ } /* if */ } /* if */ } /* if */ } /* if */ } /* if */ extra_info->prototyped = TRUE; set_routine_calling_method_flag(rout_type, np); return rout_type; } /* make_routine_type */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_routine_ptr routine_and_node_from_function_expr(an_expr_node_ptr expr, an_expr_node_ptr *p_node) /* expr is the expression identifying the function to call in a normal call (including a virtual call), or some other operand that might identify a function. If it is possible to determine the specific function named, return a pointer to its routine entry. Otherwise, return NULL. The expression can be an lvalue or rvalue for the function, even though calls actually always use the rvalue form. If the routine entry was identified via an enk_routine node and p_node is non-NULL, *p_node is set to point to that node; it is set to NULL in all other cases. */ { a_routine_ptr routine = NULL; an_expr_node_ptr node = NULL; expr = skip_parens(expr); if (is_operation_node(expr)) { an_expr_operator_kind op = expr->variant.operation.kind; if (op == (an_expr_operator_kind)eok_dot_static || op == (an_expr_operator_kind)eok_points_to_static) { /* A field selection of a static member. The second operand gives the function. It's an lvalue if and only if the top node is an lvalue, so we don't affect lvalue-ness with this adjustment. */ expr = expr->variant.operation.operands->next; } /* if */ } /* if */ if (is_constant_node(expr)) { a_constant_ptr con = node_constant(expr); if (!expr->is_lvalue && con_is_exact_addr_of_routine(con)) { /* Constant that is the address of a routine. */ routine = con->variant.address.variant.routine; } /* if */ } else { if (is_operation_node(expr) && node_operator_is(expr, eok_address_of)) { /* Remove "&" if present. */ expr = skip_parens(expr->variant.operation.operands); } /* if */ if (is_routine_node(expr)) { node = expr; routine = node_routine(expr); } /* if */ } /* if */ if (p_node != NULL) *p_node = node; return routine; } /* routine_and_node_from_function_expr */ #if !STANDALONE_UTILITY_PROGRAM a_type_ptr add_param_type(a_type_ptr rout_type, a_type_ptr param_type) /* Append a parameter of type param_type to the parameter list of rout_type. The updated routine type is returned. */ { a_param_type_ptr *p_ptp = &rout_type->variant.routine.extra_info ->param_type_list; uint32_t param_num = 1; while (*p_ptp != NULL) { p_ptp = &(*p_ptp)->next; param_num += 1; } /* while */ *p_ptp = make_param_type(param_type, &null_source_position); (*p_ptp)->param_num = param_num; return rout_type; } /* add_param_type */ #if GNU_VECTOR_TYPES_ALLOWED a_type_ptr make_vector_type(a_type_ptr element_type, a_targ_size_t n_elements, /* Defaulted: */ a_vector_kind kind) /* Return a tk_vector type representing a vector of length n_elements with elements of type element_type. kind is the kind of the vector (it is defaulted to vk_gnu). */ { a_type_ptr vtype = alloc_type((a_type_kind)tk_vector); vtype->size = skip_typerefs(element_type)->size*n_elements; check_assertion_or_expect_error( vtype->size < (a_targ_size_t)targ_maximum_pack_alignment); vtype->alignment = (a_targ_alignment)vtype->size; vtype->variant.vector.element_type = element_type; vtype->variant.vector.kind = kind; return vtype; } /* make_vector_type */ a_type_ptr make_scalable_vector_type(a_type_ptr element_type, uint8_t n_tuple_elements) /* Return a tk_scalable_vector type representing a vector of n_tuple_elements with elements of type element_type. */ { a_type_ptr vtype = alloc_type(tk_scalable_vector); /* Scalable vector types are sizeless. */ vtype->size = 0; vtype->alignment = 0; vtype->variant.scalable_vector.element_type = element_type; vtype->variant.scalable_vector.tuple_elements = n_tuple_elements; return vtype; } /* make_scalable_vector_type */ a_type_ptr scalable_vector_count_type(void) /* Make or find a type entry for a scalable vector count type and return a pointer to it. */ { a_type_ptr pit; if (il_scalable_vector_count_type != NULL) { /* The type has previously been created, and can be reused. */ pit = il_scalable_vector_count_type; } else { /* The type must be created. */ il_scalable_vector_count_type = pit = alloc_type(tk_scalable_vector_count); } /* if */ return pit; } /* scalable_vector_count_type */ a_type_ptr make_riscv_vector_type(a_type_ptr element_type, int8_t length_multiplier, uint8_t tuple_elements) /* Return a tk_riscv_vector type representing a vector of type element_type with the specified length multiplier and number of tuple elements. */ { a_type_ptr vtype = alloc_type(tk_riscv_vector); /* RISC-V vector types are sizeless. */ vtype->size = 0; vtype->alignment = 0; vtype->variant.riscv_vector.element_type = element_type; vtype->variant.riscv_vector.length_multiplier = length_multiplier; vtype->variant.riscv_vector.tuple_elements = tuple_elements; return vtype; } /* make_riscv_vector_type */ a_type_ptr modal_8bit_floating_point_type(void) /* Make or find a type entry for a modal 8-bit floating-point type and return a pointer to it. */ { a_type_ptr tp; if (il_modal_8bit_floating_point_type != NULL) { /* The type has previously been created, and can be reused. */ tp = il_modal_8bit_floating_point_type; } else { /* The type must be created. */ il_modal_8bit_floating_point_type = tp = alloc_type(tk_mfp8); set_type_size(tp); } /* if */ return tp; } /* modal_8bit_floating_point_type */ a_type_ptr float8e4m3_type(void) /* Make or find a type entry for the RISC-V OFP8 E4M3 8-bit floating-point type and return a pointer to it. */ { a_type_ptr tp; if (il_float8e4m3_type != NULL) { /* The type has previously been created, and can be reused. */ tp = il_float8e4m3_type; } else { /* The type must be created. */ il_float8e4m3_type = tp = alloc_type(tk_float8e4m3); set_type_size(tp); } /* if */ return tp; } /* float8e4m3_type */ a_type_ptr float8e5m2_type(void) /* Make or find a type entry for the RISC-V OFP8 E5M2 8-bit floating-point type and return a pointer to it. */ { a_type_ptr tp; if (il_float8e5m2_type != NULL) { /* The type has previously been created, and can be reused. */ tp = il_float8e5m2_type; } else { /* The type must be created. */ il_float8e5m2_type = tp = alloc_type(tk_float8e5m2); set_type_size(tp); } /* if */ return tp; } /* float8e5m2_type */ void eliminate_boolean_vector(a_type_ptr *p_type) /* If *p_type (p_type is assumed non-NULL) is a (possibly cv-qualified) GNU vector type marked as a boolean vector, replace *p_type by a corresponding vector type that is not so marked. The replacement preserves the vector's kind (vk_gnu, vk_ext, ...) and, for vk_ext, its size_constant. */ { a_type_ptr tp = *p_type, utp = skip_typerefs(tp); if (type_is(utp, tk_vector) && utp->variant.vector.is_boolean_vector) { a_vector_kind vkind = utp->variant.vector.kind; a_type_qualifier_set tqs = get_type_qualifiers(tp); a_type_ptr new_type = make_vector_type(utp->variant.vector.element_type, num_vector_elements(utp), vkind); if (vkind == vk_ext) { /* The element count (and hence size_constant) is unchanged. */ new_type->variant.vector.size_constant = utp->variant.vector.size_constant; } /* if */ *p_type = make_qualified_type(new_type, tqs); } /* if */ } /* eliminate_boolean_vector */ #endif /* GNU_VECTOR_TYPES_ALLOWED */ a_type_ptr make_class_template_placeholder(a_symbol_ptr class_template, a_source_position *pos, /* Defaulted: */ a_symbol_ptr bound_template) /* Create a type entry representing a class template used for C++ 17 class template argument deduction. The result is a special kind of tk_template_param entry. pos is used to establish the type entry's position information. When class_template is a type template template parameter, bound_template (or the current argument bound to class_template if bound_template is NULL) is recorded in the supplement's template_symbol field. */ { a_symbol_ptr sym; a_type_ptr type = alloc_type((a_type_kind)tk_template_param); a_template_param_type_supplement_ptr tptsp = type->variant.template_param.extra_info; a_template_param_coordinate_ptr tpcp = &tptsp->coordinates; a_template_symbol_supplement_ptr tssp = template_supplement_for_symbol(class_template); if (bound_template == NULL) { /* When not created by placeholder substitution, check whether an enclosing instantiation has bound an argument to a template template parameter (when unbound, argument_template points to class_template itself). */ a_symbol_ptr arg_sym = template_argument_if_template_template_param( class_template); if (arg_sym != class_template) { bound_template = arg_sym; } /* if */ } /* if */ if (!alias_ctad_enabled && alias_ctad_allowed && is_alias_template_symbol(class_template)) { if (clang_mode || is_class_template_but_not_alias_symbol(symbol_for( skip_simple_alias_templates(tssp->il_template_entry)))) { /* Alias-template CTAD is allowed by Clang in pre-C++20 modes; GCC only allows it for simple alias templates. */ pos_warning(ec_alias_ctad_is_cpp20, pos); } else { pos_error(ec_alias_ctad_is_cpp20, pos); } } /* if */ /* Use the class template name as the placeholder name. */ sym = alloc_symbol((a_symbol_kind)sk_type, class_template->header, pos); /* If the class template symbol is an error symbol, the class template placeholder is also implicitly in an error state. */ if (class_template->is_error) { sym->is_error = TRUE; } /* if */ sym->decl_scope = class_template->decl_scope; sym->variant.type.ptr = type; tpcp->depth = CLASS_TEMPLATE_PLACEHOLDER_NESTING_DEPTH; /* For a template template parameter, CTAD keeps using the parameter's own parameter list and defaults, while substitution goes through the template bound to it. */ tptsp->constraint.class_template_symbol = class_template; tptsp->template_symbol = bound_template; set_type_size(type); set_source_corresp(&type->source_corresp, sym); if (!class_template->is_error && tssp->il_template_entry->source_corresp.parent_scope != NULL) { /* Copy the parent scope from the class template. */ set_parent_scope(&type->source_corresp, iek_type, tssp->il_template_entry->source_corresp.parent_scope); type->source_corresp.is_class_member = tssp->il_template_entry->source_corresp.is_class_member; } /* if */ /* If the template originates from a module, ensure its definition and specializations are loaded if available. */ if (tssp->il_template_entry != NULL) { a_template_ptr templ = tssp->il_template_entry; if (has_pending_template_definition_from_module(templ)) { (void)load_template_definition_from_module(templ); } else if (has_pending_template_specializations_from_module(templ)) { (void)load_template_specializations_from_module(templ); } /* if */ } /* if */ return type; } /* make_class_template_placeholder */ a_type_ptr make_auto_type(a_source_position *pos, a_boolean is_decltype_auto) /* Create a type entry representing the "auto" or "decltype(auto)" type specifier (depending on the is_decltype_auto flag) and return it. The result is a special kind of tk_template_param entry. pos is used to establish the type entry's position information. */ { a_type_ptr type = alloc_type((a_type_kind)tk_template_param); a_template_param_coordinate_ptr tpcp = &type->variant.template_param.extra_info->coordinates; type->source_corresp.assoc_info = (char*)make_unnamed_symbol((a_symbol_kind)sk_type, pos); symbol_for(type)->variant.type.ptr = type; tpcp->depth = AUTO_TYPE_NESTING_DEPTH; tpcp->position = is_decltype_auto ? DECLTYPE_AUTO_POS_NUMBER : PLAIN_AUTO_TYPE_POS_NUMBER; set_type_size(type); return type; } /* make_auto_type */ a_type_ptr add_placeholder_typeref(a_type_ptr tp, a_boolean is_decltype_auto) /* Return a typeref entry pointing to the given type. The typeref entry represents a deduced "auto" type (if is_decltype_auto is FALSE) or a deduced "decltype(auto)" type (if is_decltype_auto is TRUE). */ { a_type_ptr type = alloc_type((a_type_kind)tk_typeref); type->variant.typeref.type = tp; if (is_decltype_auto) { type->variant.typeref.kind = trk_is_deduced_decltype_auto; } else { type->variant.typeref.kind = trk_is_deduced_auto; } /* if */ return type; } /* add_placeholder_typeref */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_base_class_derivation_ptr preferred_virtual_derivation_of( a_base_class_ptr bcp) /* Return a pointer to the base class derivation entry associated with base class bcp that is marked "preferred", namely, the one with the greatest accessibility of a public member in the context of the most derived class. There must be a derivation so marked. */ { a_base_class_derivation_ptr bcdp = bcp->derivation; while (!bcdp->preferred) { bcdp = bcdp->next; check_assertion_str2(bcdp != NULL, "preferred_virtual_derivation_of:", "no preferred derivation"); } /* while */ return bcdp; } /* preferred_virtual_derivation_of */ #if !STANDALONE_UTILITY_PROGRAM #if DEBUG static a_base_class_derivation_ptr direct_virtual_derivation_of( a_base_class_ptr bcp) /* bcp is virtual base class assumed to have its direct flag set to TRUE. Return a pointer to the associated base class derivation entry that also has direct set to TRUE. */ { a_base_class_derivation_ptr bcdp = bcp->derivation; while (!bcdp->direct) { bcdp = bcdp->next; /* Assertion will fail if direct flag has not been set. */ check_assertion_str(bcdp != NULL, "direct_virtual_derivation_of: no direct derivation"); } /* while */ return bcdp; } /* direct_virtual_derivation_of */ #endif /* DEBUG */ a_translation_unit_ptr trans_unit_for_source_corresp( a_source_correspondence *scp) /* Return the translation unit associated with the indicated source correspondence. The entity must have an associated symbol. */ { a_translation_unit_ptr tup; a_symbol_ptr sym = (a_symbol_ptr)(scp->assoc_info); check_assertion_str(in_front_end, "trans_unit_for_source_corresp: not in front end"); check_assertion_str(sym != NULL, "trans_unit_for_source_corresp: no assoc symbol"); if (!symbol_has_trans_unit_ptr(sym)) { /* There must be some previous error. */ check_assertion(is_at_least_one_error()); /* Pick an arbitrary translation unit, primary or secondary as appropriate. */ if (in_secondary_trans_unit(scp)) { tup = translation_units->next; } else { tup = translation_units; } /* if */ } else { tup = trans_unit_for_symbol(sym); } /* if */ return tup; } /* trans_unit_for_source_corresp */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_routine_ptr enclosing_routine_for_local_type_or_null(a_type_ptr type) /* Given a function-local type, return a pointer to the innermost function that encloses it, or null if the type belongs to a local function prototype scope. */ { check_assertion_or_expect_error(type->source_corresp.is_local_to_function); /* For members of local classes, go up through all the containing classes to get to the class declared directly in the function. */ while (type->source_corresp.is_class_member) { type = parent_class_of(type); } /* while */ return type->source_corresp.enclosing_routine; } /* enclosing_routine_for_local_type_or_null */ a_routine_ptr enclosing_routine_for_local_type(a_type_ptr type) /* Given a function-local type, return a pointer to the innermost function that encloses it. The given type should not belong to a local function prototype scope. */ { a_routine_ptr result = enclosing_routine_for_local_type_or_null(type); check_assertion(result != NULL); return result; } /* enclosing_routine_for_local_type */ a_scope_ptr scope_for_routine_or_null(a_routine_ptr rout) /* Return the function scope for the given (defined) routine, or NULL (e.g., in cases where the memory region is not available). */ { a_scope_ptr scope; check_assertion(rout != NULL && rout->function_def_number != NULL_function_def_number); #if STANDALONE_IL_DISPLAY || \ (NONCLASS_TEMPLATE_INSTANTIATIONS_IN_SOURCE_SEQUENCE_LISTS && \ IL_SHOULD_BE_WRITTEN_TO_FILE && BACK_END_SHOULD_BE_CALLED) /* The IL display utility processes the file scope memory region first, followed by all the function scope memory regions. If there is a local expression node reference in a file-scope entry, however, the relevant function scope memory region must be loaded on the fly to satisfy that request. Similarly, when generating code, references in generated function template instances may precede the loading of the function containing the referenced entity. */ if (il_header.function_def_table[rout->function_def_number].memory_region != rout->memory_region) { read_memory_region(rout->memory_region); } /* if */ #endif /* STANDALONE_IL_DISPLAY || ... */ scope = scope_for_function_def(rout->function_def_number); return scope; } /* scope_for_routine_or_null */ a_scope_ptr scope_for_routine(a_routine_ptr rout) /* Return the function scope for the given (defined) routine. */ { a_scope_ptr scope; scope = scope_for_routine_or_null(rout); check_assertion_str(scope != NULL, "scope for routine is NULL"); check_assertion(scope->kind == (a_scope_kind)sck_function); return scope; } /* scope_for_routine */ a_scope_ptr function_scope_for_local_type(a_type_ptr type) /* Return the function scope in which the given local type is declared. The type might be declared in a block scope or local class inside that function scope. */ { a_routine_ptr rout = enclosing_routine_for_local_type(type); a_scope_ptr scope = scope_for_routine(rout); return scope; } /* function_scope_for_local_type */ static a_scope_ptr get_scope_for_list( a_scope_depth scope_level, a_source_correspondence *scp, a_scope_pointers_block_ptr *pointers_block) /* An IL entry that has been declared at the indicated scope depth and with the source-correspondence pointed to by scp is to be added to (or is already on) a list pointed to by an IL scope entry. Return a pointer to that IL scope entry, along with the associated pointers-block. When scope_level is NO_SCOPE_DEPTH, the scope is either the file scope or the scope associated with the class or namespace of which the entry is a member, and the scope need not be on the scope stack. That mode cannot be used for function-local entities unless they are class or namespace members. *pointers_block is returned NULL if there is no pointers block (anymore) for the scope, which means no last-pointer is being maintained (anymore). When used from outside of the front end, this function may return a NULL scope (as no further IL scopes can be created) if the returned scope would have been a scope from the scope stack (i.e., not a file, namespace, or class scope). */ { a_scope_ptr sp = NULL; a_namespace_ptr nsp = NULL; a_type_ptr class_type = NULL; if (scope_level == NO_SCOPE_DEPTH) { /* No scope depth is specified, so either use the namespace, class, or file scope. */ if (scp->is_class_member) { /* Compute the scope and pointers-block for the scope associated with the parent class. */ if (!C_mode()) { class_type = scp_parent_class(scp); } else { scope_level = DEPTH_OF_FILE_SCOPE; } /* if */ } else if (!C_mode() && scp->name_linkage == (a_name_linkage_kind)nlk_external) { /* extern "C" functions and variables are always on the file scope list, even if they are namespace members. */ scope_level = DEPTH_OF_FILE_SCOPE; } else { /* Compute the scope and pointers-block for the namespace scope if this is a namespace member or the file scope otherwise. */ nsp = scp_parent_namespace_or_null(scp); if (nsp == NULL) { check_assertion_or_expect_error(in_file_scope(scp)); scope_level = DEPTH_OF_FILE_SCOPE; } /* if */ } /* if */ } /* if */ if (class_type != NULL) { check_assertion_str(!C_mode(), "get_scope_for_list: class scope in C mode"); #if !STANDALONE_UTILITY_PROGRAM if (in_front_end) { sp = class_type_supp(scp_parent_class(scp))->assoc_scope; if (!scope_is_null_or_placeholder(sp)) { scope_level = sp->depth_in_scope_stack; } /* if */ if (scope_level == NO_SCOPE_DEPTH) { /* The class has already been popped off the scope stack. */ *pointers_block = NULL; } else { check_assertion(trans_unit_for_scope[sp->number] == curr_translation_unit); a_scope_stack_entry_ptr ssep = &scope_stack[scope_level]; *pointers_block = assoc_pointers_block_of(ssep); } /* if */ } else #endif /* !STANDALONE_UTILITY_PROGRAM */ { #if DO_IL_LOWERING /* After IL lowering is performed, class scope members are promoted to file scope (see promote_class_members in lower_il.c for more information). Thus, after IL lowering return the file scope. */ sp = il_header.primary_scope; #else /* !DO_IL_LOWERING */ sp = class_type_supp(scp_parent_class(scp))->assoc_scope; #endif /* DO_IL_LOWERING */ *pointers_block = NULL; } /* if */ } else if (nsp != NULL) { /* Use the IL scope from the namespace. */ sp = nsp->variant.assoc_scope; *pointers_block = &symbol_supplement_for_namespace(nsp)->pointers_block; } else if (scope_level == DEPTH_OF_FILE_SCOPE) { /* The file scope. This is done without using the scope stack to deal with some cases where the file scope is not on the stack. It works fine also for the cases where the file scope is on the scope stack. */ if (in_front_end) { check_assertion(curr_translation_unit != NULL); sp = curr_translation_unit->primary_scope; *pointers_block = &curr_translation_unit->file_scope_pointers_block; } else { sp = il_header.primary_scope; } /* if */ check_assertion(sp != NULL && sp->kind == sck_file); } else { #if !STANDALONE_UTILITY_PROGRAM if (in_front_end) { /* Use the IL scope associated with scope_level on the scope_stack. */ check_assertion(scope_level >= 0 && scope_level <= depth_scope_stack); a_scope_stack_entry_ptr ssep = &scope_stack[scope_level]; sp = ensure_il_scope_exists(ssep); *pointers_block = assoc_pointers_block_of(ssep); } else #endif /* !STANDALONE_UTILITY_PROGRAM */ /* Do not add code here. */ { *pointers_block = NULL; } /* if */ } /* if */ return sp; } /* get_scope_for_list */ #if !STANDALONE_UTILITY_PROGRAM a_boolean may_be_added_to_types_list(a_type_ptr type_ptr, a_scope_depth decl_level) /* Return TRUE unless there is any reason why type_ptr should not be added to the types-list for the scope associated with the indicated scope depth. The given type should have an associated symbol (e.g., it cannot be an "int" type or a template parameter type). Note: the type may already be on the list; this routine is also called when it's to be moved to another position in the list. */ { a_boolean may_be_added = TRUE; a_scope_stack_entry_ptr ssep = NULL; if (is_or_contains_error_type(type_ptr) || symbol_for(type_ptr)->is_error) { may_be_added = FALSE; } else if (is_immediate_class_type(type_ptr) || is_immediate_enum_type(type_ptr)) { if (decl_level != NO_SCOPE_DEPTH) ssep = &scope_stack[decl_level]; if (C_mode()) { check_assertion(ssep != NULL); if (type_ptr->declared_in_function_prototype && ssep->kind != (a_scope_kind)sck_func_prototype) { /* If the tag was declared in a prototype scope and is now being resolved within the function, as in int f(struct f p) {struct f{int a;}; ... } we may assume the type entry has already been entered on the types list. */ may_be_added = FALSE; } /* if */ } else if (ssep != NULL) { if (ssep->kind == (a_scope_kind)sck_template_declaration) { /* Must be an error case -- e.g., a class definition within a template parameter declaration. Don't try to enter the class in the IL. */ may_be_added = FALSE; } else if (ssep->kind == (a_scope_kind)sck_template_instantiation) { /* In some cases, nonreal nested class types are associated with a template instantiation scope. However, the IL entry should not be recorded there since there is no associated a_scope entry. */ may_be_added = FALSE; } else if (type_ptr->source_corresp.is_class_member) { if (ssep->kind != (a_scope_kind)sck_class_struct_union || ssep->assoc_type != parent_class_of(type_ptr)) { /* May be an out-of-class definition of a C++ nested class. It's already on the list. */ may_be_added = FALSE; } /* if */ } else if (ssep->kind == (a_scope_kind)sck_class_reactivation || ssep->kind == (a_scope_kind)sck_namespace_reactivation) { /* A closure class that was created in a class or namespace reactivation scope. This was added to the type list specially and should not be handled here. */ check_assertion(class_type_supp(type_ptr)->is_lambda_closure_class); may_be_added = FALSE; } else if (type_ptr->source_corresp.is_local_to_function && ssep->in_prototype_instantiation && !prototype_instantiations_in_il) { /* A local type parsed during the prototype instantiation of a function when prototype instantiations aren't recorded in the IL: Do not add the local type to the IL since it may contain references to template parameters. */ may_be_added = FALSE; } /* if */ } else if (type_ptr->source_corresp.is_class_member) { /* Check for a nested class that is being defined after the definition of its parent class has been completed. In such cases leave the type entry at the current place in the types list. */ a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; check_assertion(is_immediate_class_type(type_ptr)); sp = get_scope_for_list(NO_SCOPE_DEPTH, &type_ptr->source_corresp, &pointers_block); if (sp->depth_in_scope_stack == NO_SCOPE_DEPTH) { may_be_added = FALSE; } /* if */ } /* if */ } /* if */ return may_be_added; } /* may_be_added_to_types_list */ #if NAMED_REGISTERS_ALLOWED STATIC_THREAD a_variable_ptr named_register_variables[NUM_NAMED_REGISTERS+1]; void record_named_register_storage_class(a_variable_ptr var, a_named_register_id register_id, a_boolean is_redecl, a_source_position *pos) /* Record the given named-register storage class id in the given variable. If it conflicts with a different construct or a previous declaration, issue an error at the given position. If we're processing a redeclaration, is_redecl is TRUE. Also check that the type of the variable is appropriate for the named register and issue an error if not. This function is also called for variable declarations without named-register storage class specifiers (in which case register_id is zero) so that conflicts with previous declarations of that variable can be diagnosed. */ { a_symbol_ptr sym = (a_symbol_ptr)var->source_corresp.assoc_info; if (is_redecl) { /* A redeclaration. */ if (var->has_named_register_storage_class && register_id == 0) { /* The new declaration has no named-register storage class specifier, but the previous one did. */ pos_sy_error(ec_missing_named_register_storage_class, pos, sym); } else if ((!var->has_named_register_storage_class && register_id != 0) || (var->has_named_register_storage_class && register_id != var->asm_name_or_reg.id)) { /* The new declaration has a named-register storage class but the previous declaration did not or did not have the same one. */ pos_sy_error(ec_register_storage_class_conflict, pos, sym); } /* if */ #if NAMED_ADDRESS_SPACES_ALLOWED } else if (named_address_spaces_enabled && register_id != 0 && type_qualified_with_named_address_space(var->type)) { pos_error(ec_register_in_address_space, pos); #endif /* NAMED_ADDRESS_SPACES_ALLOWED */ #if GNU_EXTENSIONS_ALLOWED || REDEFINE_EXTNAME_PRAGMA_ENABLED } else if (!var->has_named_register_storage_class && (!var->asm_name_is_valid || var->asm_name_or_reg.name != NULL)) { /* The variable was already mapped using a GNU construct or a Sun pragma. */ pos_error(ec_aliased_variable_cannot_have_register_storage_class, pos); #endif /* GNU_EXTENSIONS_ALLOWED || REDEFINE_EXTNAME_PRAGMA_ENABLED */ } else if (register_id != 0) { a_variable_ptr prev_var = named_register_variables[register_id]; if (prev_var != NULL) { if (var != prev_var) { pos_error(ec_register_in_use, pos); } /* if */ } else if (is_array_type(var->type)) { pos_error(ec_no_named_register_for_array, pos); } else if (is_void_type(var->type)) { pos_error(ec_void_named_register, pos); } else if (named_register_storage_classes[register_id].size < skip_typerefs(var->type)->size) { pos_error(ec_register_too_small, pos); } else { #if GNU_EXTENSIONS_ALLOWED || REDEFINE_EXTNAME_PRAGMA_ENABLED var->asm_name_is_valid = FALSE; #endif /* GNU_EXTENSIONS_ALLOWED || REDEFINE_EXTNAME_PRAGMA_ENABLED */ var->has_named_register_storage_class = TRUE; var->asm_name_or_reg.id = register_id; named_register_variables[register_id] = var; } /* if */ } /* if */ } /* record_named_register_storage_class */ #endif /* NAMED_REGISTERS_ALLOWED */ void set_parent_scope_for_type(a_type_ptr type_ptr, a_scope_depth scope_level) /* Set the parent scope for type_ptr. Normally this is done when the type is added to the types list. This routine is used if the type is not added to the type list. scope_level is the declaration scope used to determine the parent scope. When scope_level is NO_SCOPE_DEPTH, the scope is computed rather than determined directly. */ { a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; if (parent_scope_of(type_ptr) == NULL) { sp = get_scope_for_list(scope_level, &type_ptr->source_corresp, &pointers_block); set_parent_scope(&type_ptr->source_corresp, iek_type, sp); } /* if */ } /* set_parent_scope_for_type */ void add_lambda_closure_to_types_list(a_type_ptr type_ptr, a_scope_depth scope_level) /* Add the given lambda closure type to the types list for the scope specified by scope_level. If scope_level specifies a class reactivation scope, instead of the normal add_to_types_list call, special processing is done to add the type to the end of the types list for the enclosing class. */ { a_scope_stack_entry_ptr ssep; check_assertion(scope_level != NO_SCOPE_DEPTH); ssep = scope_stack_entry_for(scope_level); if (ssep->reactivated_class_being_defined) { /* We are adding the type to a class reactivation scope, but the class is still in the process of being defined. Find the scope stack entry for the class and use that. */ a_type_ptr class_type = ssep->assoc_type; /* Note that previous_scope_of is not used because we want to consider scopes even if they are not visible. */ /*lint -e{440}*/ for (ssep = scope_stack_entry_for(scope_level-1); scope_depth_of(ssep) >= DEPTH_OF_FILE_SCOPE; ssep--) { if (ssep->kind == (a_scope_kind)sck_class_struct_union && ssep->assoc_type == class_type) { break; } /* if */ } /* for */ check_assertion(ssep != NULL); scope_level = scope_depth_of(ssep); } /* if */ if (ssep->kind == (a_scope_kind)sck_class_reactivation) { /* When a lambda appears in a class reactivation scope it must be added to the types list of the class that was reactivated. */ a_scope_ptr sp; a_type_ptr tp; set_parent_scope_for_type(type_ptr, scope_level); sp = parent_scope_of(type_ptr); tp = sp->types; if (tp != NULL) { /* Find the last entry on the types list. */ for (tp = sp->types; tp->next != NULL; tp = tp->next) {} /* Add the closure type to the end of the list. */ tp->next = type_ptr; } else { sp->types = type_ptr; } /* if */ } else if (ssep->kind == (a_scope_kind)sck_namespace_reactivation) { /* When a lambda appears in a namespace reactivation scope it must be added to the types list of the namespace that was reactivated. */ a_scope_ptr sp; a_namespace_ptr nsp = scope_stack[scope_level].assoc_namespace; a_scope_pointers_block_ptr pointers_block; sp = nsp->variant.assoc_scope; set_parent_scope(&type_ptr->source_corresp, iek_type, sp); pointers_block = scope_stack[scope_level].assoc_pointers_block; if (sp->types == NULL) { sp->types = type_ptr; } else { a_type_ptr prev_type; prev_type = pointers_block->last_type; prev_type->next = type_ptr; } /* if */ pointers_block->last_type = type_ptr; } else { /* Normal case -- just use the normal add_to_types_list process. */ add_to_types_list(type_ptr, scope_level); } /* if */ } /* add_lambda_closure_to_types_list */ void add_to_types_list(a_type_ptr type_ptr, a_scope_depth scope_level) /* Add the given type to the types list for the scope corresponding to scope_level. When scope_level is NO_SCOPE_DEPTH, the scope is computed rather than determined directly. */ { a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; /* Get a pointer to the current or file scope entry. */ /* Note: If we are currently inside the declaration list for the old-style parameters of a function -- e.g., in the "struct" declaration in int f(a) struct s {int b;} a; { } -- the type should be entered in the prototype scope; that makes the type available in the memory region of the function's parent, which is necessary for type-compatibility checking of parameters. A similar situation applies for type declarations that are part of function prototypes, as in int f(struct s {int b;} a); The prototype scope is hardly ever needed, and therefore it is not allocated by default. It is allocated in ensure_il_scope_exists, which is called by get_scope_for_list. */ sp = get_scope_for_list(scope_level, &type_ptr->source_corresp, &pointers_block); if (sp == NULL) { /* May be an error case. */ } else { a_type_ptr prev_type; /* Add the type to the list of types for this scope. */ if (sp->types == NULL) { sp->types = type_ptr; prev_type = NULL; } else if (pointers_block != NULL) { prev_type = pointers_block->last_type; prev_type->next = type_ptr; } else { /* The scope stack entry is no longer on the stack, so just look for the end of the types list and add the new type. */ prev_type = sp->types; while (prev_type->next != NULL) prev_type = prev_type->next; prev_type->next = type_ptr; } /* if */ type_ptr->next = NULL; /* Record the parent scope. Only update the parent the first time the entry is added to the IL. */ if (!has_parent_scope(type_ptr)) { set_parent_scope(&type_ptr->source_corresp, iek_type, sp); } /* if */ if (pointers_block != NULL) pointers_block->last_type = type_ptr; /* In some cases we record the preceding entry on the list. This allows us to optimize move_to_end_of_types_list for speed. */ if (is_immediate_class_type(type_ptr) && type_ptr->source_corresp.assoc_info != NULL) { a_class_symbol_supplement_ptr cssp = symbol_supplement_for_class(type_ptr); if (cssp != NULL) { cssp->prev_entry_on_types_list = prev_type; } /* if */ } /* if */ #if DEBUG if (db_flag_is_set("dump_type_lists")) { fprintf(f_debug, "Added to types list: "); db_abbreviated_type(type_ptr); fprintf(f_debug, "\n"); } /* if */ #endif /* DEBUG */ } /* if */ } /* add_to_types_list */ void move_to_end_of_types_list(a_type_ptr type_ptr, a_scope_depth scope_level) /* Move the indicated type, which is already on the types list of an IL scope, to the end of that list. Use scope_level to find the appropriate IL scope. When scope_level is NO_SCOPE_DEPTH, the scope is computed rather than determined directly. */ { a_scope_ptr sp = parent_scope_of(type_ptr); a_scope_pointers_block_ptr pointers_block = NULL; a_type_ptr tp, prev_tp; if (sp == NULL) { sp = get_scope_for_list(scope_level, &type_ptr->source_corresp, &pointers_block); } else if (sp->depth_in_scope_stack != NO_SCOPE_DEPTH) { a_scope_stack_entry *ssep = &scope_stack[sp->depth_in_scope_stack]; pointers_block = assoc_pointers_block_of(ssep); } else { if (scope_is(sp, sck_class_struct_union)) { pointers_block = &class_symbol_supp(symbol_for(sp->variant.assoc_type)) ->pointers_block; } else { check_assertion(scope_is(sp, sck_namespace) || scope_is(sp, sck_namespace_extension)); pointers_block = &symbol_for(sp->variant.assoc_namespace) ->variant.namespace_info.extra_info ->pointers_block; } /* if */ } /* if */ if (sp == NULL || pointers_block == NULL) { /* May be an error case. */ } else { if (pointers_block->last_type == type_ptr) { /* It's already the last entry on the list. */ } else { a_class_symbol_supplement_ptr cssp = NULL; if (is_immediate_class_type(type_ptr) && type_ptr->source_corresp.assoc_info != NULL) { cssp = symbol_supplement_for_class(type_ptr); } /* if */ if (cssp != NULL && cssp->prev_entry_on_types_list != NULL && cssp->prev_entry_on_types_list->next == type_ptr) { /* Class types usually have a valid "previous entry" pointer. */ prev_tp = cssp->prev_entry_on_types_list; } else { /* Scan the list until a match is found. */ prev_tp = NULL; tp = sp->types; while (tp != type_ptr) { prev_tp = tp; tp = tp->next; check_assertion_str2(tp != NULL, "move_to_end_of_types_list:", "cannot find type on types list"); } /* while */ } /* if */ /* Link around the entry. */ if (prev_tp == NULL) { sp->types = type_ptr->next; } else { prev_tp->next = type_ptr->next; } /* if */ if (type_ptr->next != NULL && is_immediate_class_type(type_ptr->next)) { /* Update the "previous entry" pointer of the next type entry (if applicable). */ a_class_symbol_supplement_ptr next_cssp = symbol_supplement_for_class(type_ptr->next); if (next_cssp != NULL) { next_cssp->prev_entry_on_types_list = prev_tp; } /* if */ } /* if */ /* Reenter it onto the end of the list. */ pointers_block->last_type->next = type_ptr; pointers_block->last_type = type_ptr; type_ptr->next = NULL; } /* if */ #if DEBUG if (db_flag_is_set("dump_type_lists")) { fprintf(f_debug, "Moved to end of list: "); db_abbreviated_type(type_ptr); fprintf(f_debug, "\n"); } /* if */ #endif /* DEBUG */ } /* if */ } /* move_to_end_of_types_list */ void record_fundamental_types_copied_from_secondary_IL(void) /* This routine should be called after all the secondary translation units have been merged into the primary translation unit. This merging might have copied fundamental types to the primary translation unit. This routine records any such updates so that future calls to e.g. "wchar_t_type()" will return a pre-existing fundamental type if one exists. Must only be called for the primary translation unit. */ { int k; check_assertion(is_primary_translation_unit); for (k = 0; k < (int)ik_last; ++k) { int_types[k] = primary_int_type((an_integer_kind)k); signed_int_types[k] = primary_signed_int_type((an_integer_kind)k); #if MICROSOFT_EXTENSIONS_ALLOWED microsoft_sized_int_types[k] = primary_microsoft_sized_int_type((an_integer_kind)k); microsoft_sized_signed_int_types[k] = primary_microsoft_sized_signed_int_type((an_integer_kind)k); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* for */ il_wchar_t_type = primary_wchar_t_type(); il_char8_t_type = primary_char8_t_type(); il_char16_t_type = primary_char16_t_type(); il_char32_t_type = primary_char32_t_type(); il_standard_nullptr_type = primary_standard_nullptr_type(); il_managed_nullptr_type = primary_managed_nullptr_type(); #if C99_IL_EXTENSIONS_SUPPORTED il_bool_type = primary_bool_type(); #endif /* C99_IL_EXTENSIONS_SUPPORTED */ #if FIXED_POINT_ALLOWED for (k = 0; k < (int)fpp_last; ++k) { int l, m, n; a_fixed_point_type_descr descr; descr.precision = (a_fixed_point_precision)k; for (l = 0; l < 2; ++l) { descr.is_unsigned = (a_boolean)l; for (m = 0; m < 2; ++m) { descr.is_fract_type = (a_boolean)m; for (n = 0; n < 2; ++n) { descr.saturating = (a_boolean)n; fixed_point_types[k][l][m][n] = primary_fixed_point_type(descr); } /* for */ } /* for */ } /* for */ } /* for */ #endif /* FIXED_POINT_ALLOWED */ for (k = 0; k < (int)fk_last; ++k) { float_types[k] = primary_float_type((a_float_kind)k); #if C99_IL_EXTENSIONS_SUPPORTED complex_types[k] = primary_complex_type((a_float_kind)k); imaginary_types[k] = primary_imaginary_type((a_float_kind)k); #endif /* C99_IL_EXTENSIONS_SUPPORTED */ } /* for */ } /* record_fundamental_types_copied_from_secondary_IL */ a_const_char* get_type_name(a_type_ptr tp) /* If the given type already has an associated name, return that. Otherwise, generate a name in a temporary buffer and return that. Names allocated in a temporary buffer need to be copied if they are to be permanent. */ { a_const_char *result; result = tp->source_corresp.name; if (result == NULL) { an_il_to_str_output_control_block octl; clear_il_to_str_output_control_block(&octl); octl.output_str = put_str_to_temp_text_buffer_octl; octl.gen_compilable_code = TRUE; pos_in_temp_text_buffer = 0; form_type_first_part_simple(tp, /*under_lhs_declarator=*/FALSE, /*need_trailing_space=*/FALSE, &octl); form_type_second_part_simple(tp, /*under_lhs_declarator=*/FALSE, &octl); put_ch_to_temp_text_buffer('\0'); result = temp_text_buffer; } /* if */ return result; } /* get_type_name */ a_type_ptr integer_type(an_integer_kind kind) /* Make or find a type entry for an integer type of the indicated kind, and return a pointer to it. */ { a_type_ptr pit; check_assertion_str(!is_bit_precise_kind(kind), "integer_type: bad bit-precise kind"); if (int_types[kind] != NULL) { /* The type has previously been created, and can be reused. */ pit = int_types[kind]; } else { /* The type must be created. */ int_types[kind] = pit = alloc_type((a_type_kind)tk_integer); pit->variant.integer.int_kind = kind; set_type_size(pit); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)pit, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(pit); } /* if */ return pit; } /* integer_type */ a_type_ptr bit_precise_integer_type(a_targ_size_t bit_width, a_boolean is_unsigned, a_boolean explicitly_signed) /* Create a type entry for a bit-precise integer type of the indicated width and signedness. */ { a_type_ptr pit; check_assertion(bit_width != 0); pit = alloc_type(tk_integer); pit->variant.integer.int_kind = is_unsigned ? ik_unsigned_bit_precise : ik_bit_precise; pit->variant.integer.explicitly_signed = explicitly_signed; integer_type_supp(pit)->bit_width = bit_width; set_type_size(pit); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)pit, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ return pit; } /* bit_precise_integer_type */ a_type_ptr dependent_bit_precise_integer_type(a_constant_ptr bit_width_constant, a_boolean is_unsigned) /* Create a dependent _BitInt type whose width is represented by bit_width_constant. */ { a_type_ptr pit; check_assertion(bit_width_constant != NULL); pit = alloc_type(tk_template_param); pit->variant.template_param.kind = tptk_bit_precise_int; pit->variant.template_param.is_unsigned_bit_precise_int = is_unsigned; pit->variant.template_param.extra_info->coordinates.depth = BIT_PRECISE_INT_NESTING_DEPTH; pit->variant.template_param.extra_info->constraint.bit_width_constant = bit_width_constant; set_type_size(pit); return pit; } /* dependent_bit_precise_integer_type */ #if MICROSOFT_EXTENSIONS_ALLOWED a_type_ptr microsoft_sized_integer_type(an_integer_kind kind) /* Make or find a type entry for a sized integer type (__intN) of the indicated kind, and return a pointer to it. (This is for the case of a "__intN" type that Microsoft Visual C++ 6.0 treats as a distinct built-in type as opposed to just a typedef for another integral type.) */ { a_type_ptr pit; if (microsoft_sized_int_types[kind] != NULL) { /* The type has previously been created, and can be reused. */ pit = microsoft_sized_int_types[kind]; } else { /* The type must be created. */ pit = alloc_type((a_type_kind)tk_integer); pit->variant.integer.int_kind = kind; pit->variant.integer.microsoft_sized_int_type = TRUE; set_type_size(pit); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)pit, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ microsoft_sized_int_types[kind] = pit; record_builtin_type(pit); } /* if */ return pit; } /* microsoft_sized_integer_type */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ a_type_ptr signed_integer_type(an_integer_kind kind) /* Make or find a type entry for an explicitly signed integer type of the indicated kind, and return a pointer to it. Keeping track of the difference between, e.g., a plain "int" and a "signed int" is necessary because the two may be handled differently for bit fields. Should only be called for kinds ik_short, ik_int, ik_long, ik_long_long, and ik_int128. */ { a_type_ptr pit; if (signed_int_types[kind] != NULL) { /* The type has previously been created, and can be reused. */ pit = signed_int_types[kind]; } else { /* The type must be created. */ #if CHECKING if (kind != (an_integer_kind)ik_short && kind != (an_integer_kind)ik_int && kind != (an_integer_kind)ik_long #if LONG_LONG_ALLOWED && kind != (an_integer_kind)ik_long_long #endif /* LONG_LONG_ALLOWED */ #if INT128_EXTENSIONS_ALLOWED && kind != (an_integer_kind)ik_int128 #endif /* INT128_EXTENSIONS_ALLOWED */ ) { internal_error("signed_integer_type: bad int kind"); } /* if */ #endif /* CHECKING */ signed_int_types[kind] = pit = alloc_type((a_type_kind)tk_integer); pit->variant.integer.int_kind = kind; pit->variant.integer.explicitly_signed = TRUE; set_type_size(pit); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)pit, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(pit); } /* if */ return pit; } /* signed_integer_type */ #if MICROSOFT_EXTENSIONS_ALLOWED a_type_ptr microsoft_sized_signed_integer_type(an_integer_kind kind) /* Make or find a type entry for an explicitly signed integer type of the indicated kind, and return a pointer to it. Keeping track of the difference between, e.g., a plain "__int32" and a "signed __int32" is necessary because the two may be handled differently for bit fields. Should only be called for the kinds corresponding to __int16, __int32 and __int64. (This is for the case of a "__intN" type that Microsoft Visual C++ 6.0 treats as a distinct built-in type as opposed to just a typedef for another integral type.) */ { a_type_ptr pit; if (microsoft_sized_signed_int_types[kind] != NULL) { /* The type has previously been created, and can be reused. */ pit = microsoft_sized_signed_int_types[kind]; } else { /* The type must be created. */ #if CHECKING if (kind != targ_int16_int_kind && kind != targ_int32_int_kind && kind != targ_int64_int_kind) { internal_error("microsoft_sized_signed_integer_type: bad int kind"); } /* if */ #endif /* CHECKING */ pit = alloc_type((a_type_kind)tk_integer); pit->variant.integer.int_kind = kind; pit->variant.integer.explicitly_signed = TRUE; pit->variant.integer.microsoft_sized_int_type = TRUE; set_type_size(pit); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)pit, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ microsoft_sized_signed_int_types[kind] = pit; record_builtin_type(pit); } /* if */ return pit; } /* microsoft_sized_signed_integer_type */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ a_type_ptr other_signedness_integer_type(an_integer_kind ikind) /* Return the integer type that is paired with the one indicated by ikind and has the opposite signedness. For example, ik_unsigned_long as input produces ik_long as output. */ { an_integer_kind nkind = (an_integer_kind)ik_none; switch (ikind) { case ik_char: if (targ_has_signed_chars) { nkind = (an_integer_kind)ik_unsigned_char; } else { nkind = (an_integer_kind)ik_signed_char; } /* if */ break; case ik_signed_char: nkind = (an_integer_kind)ik_unsigned_char; break; case ik_unsigned_char: nkind = (an_integer_kind)ik_signed_char; break; case ik_short: nkind = (an_integer_kind)ik_unsigned_short; break; case ik_unsigned_short: nkind = (an_integer_kind)ik_short; break; case ik_int: nkind = (an_integer_kind)ik_unsigned_int; break; case ik_unsigned_int: nkind = (an_integer_kind)ik_int; break; case ik_long: nkind = (an_integer_kind)ik_unsigned_long; break; case ik_unsigned_long: nkind = (an_integer_kind)ik_long; break; #if LONG_LONG_ALLOWED case ik_long_long: nkind = (an_integer_kind)ik_unsigned_long_long; break; case ik_unsigned_long_long: nkind = (an_integer_kind)ik_long_long; break; #endif /* LONG_LONG_ALLOWED */ #if INT128_EXTENSIONS_ALLOWED case ik_int128: nkind = (an_integer_kind)ik_unsigned_int128; break; case ik_unsigned_int128: nkind = (an_integer_kind)ik_int128; break; #endif /* INT128_EXTENSIONS_ALLOWED */ default: unexpected_condition_str("other_signedness_integer_type: bad kind"); } /* switch */ return integer_type(nkind); } /* other_signedness_integer_type */ a_type_ptr wchar_t_type(void) /* Make or find a type entry for a wchar_t type and return a pointer to it. This is only used when wchar_t is a distinct type. Note that in Microsoft mode (even Microsoft C mode) there can be a distinct wchar_t type even when wchar_t_is_keyword is FALSE because the __wchar_t keyword can always be used. But when wchar_t_is_keyword is FALSE, wide string literals do not have a type based on wchar_t_type. */ { a_type_ptr pit; if (il_wchar_t_type != NULL) { /* The type has previously been created, and can be reused. */ pit = il_wchar_t_type; } else { /* The type must be created. */ il_wchar_t_type = pit = alloc_type((a_type_kind)tk_integer); pit->variant.integer.int_kind = targ_wchar_t_int_kind; pit->variant.integer.wchar_t_type = TRUE; set_type_size(pit); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)pit, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(pit); } /* if */ return pit; } /* wchar_t_type */ a_type_ptr char8_t_type(void) /* Make or find a type entry for a char8_t type and return a pointer to it. This is only used when char8_t is enabled. */ { a_type_ptr pit; a_type_ptr *ptype; ptype = &il_char8_t_type; if (*ptype != NULL) { /* The type has previously been created, and can be reused. */ pit = *ptype; } else { /* The type must be created. */ *ptype = pit = alloc_type((a_type_kind)tk_integer); pit->variant.integer.int_kind = (an_integer_kind)ik_unsigned_char; pit->variant.integer.char8_t_type = TRUE; set_type_size(pit); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)pit, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(pit); } /* if */ return pit; } /* char8_t_type */ a_type_ptr char16_t_type(void) /* Make or find a type entry for a char16_t type and return a pointer to it. This is only used when char16_t is a distinct type. */ { a_type_ptr pit; a_type_ptr *ptype; ptype = &il_char16_t_type; if (*ptype != NULL) { /* The type has previously been created, and can be reused. */ pit = *ptype; } else { /* The type must be created. */ *ptype = pit = alloc_type((a_type_kind)tk_integer); pit->variant.integer.int_kind = targ_char16_t_int_kind; pit->variant.integer.char16_t_type = TRUE; set_type_size(pit); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)pit, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(pit); } /* if */ return pit; } /* char16_t_type */ a_type_ptr char32_t_type(void) /* Make or find a type entry for a char32_t type and return a pointer to it. This is only used when char32_t is a distinct type. */ { a_type_ptr pit; a_type_ptr *ptype; ptype = &il_char32_t_type; if (*ptype != NULL) { /* The type has previously been created, and can be reused. */ pit = *ptype; } else { /* The type must be created. */ *ptype = pit = alloc_type((a_type_kind)tk_integer); pit->variant.integer.int_kind = targ_char32_t_int_kind; pit->variant.integer.char32_t_type = TRUE; set_type_size(pit); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)pit, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(pit); } /* if */ return pit; } /* char32_t_type */ a_type_ptr eff_wchar_t_type(void) /* Return the effective type of wchar_t. This is a distinct type if wchar_t_is_keyword is TRUE, and the appropriate integral type otherwise. The type returned by this function is the type of wide character literals and the (cv-unqualified) type of the elements of wide string literals. */ { a_type_ptr eff_wchar_t; if (wchar_t_is_keyword) { eff_wchar_t = wchar_t_type(); } else { eff_wchar_t = integer_type((an_integer_kind)targ_wchar_t_int_kind); } /* if */ return eff_wchar_t; } /* eff_wchar_t_type */ a_type_ptr eff_char8_t_type(void) /* Return the effective type of char8_t. This is a distinct type if char8_t_enabled is TRUE and unsigned char otherwise. */ { a_type_ptr eff_char8_t; if (char8_t_enabled) { eff_char8_t = char8_t_type(); } else { eff_char8_t = integer_type((an_integer_kind)ik_unsigned_char); } /* if */ return eff_char8_t; } /* eff_char8_t_type */ a_type_ptr eff_char16_t_type(void) /* Return the effective type of char16_t. This is a distinct type if char16_t_and_char32_t_are_keywords is TRUE and the appropriate integral type otherwise. */ { a_type_ptr eff_char16_t; if (char16_t_and_char32_t_are_keywords) { eff_char16_t = char16_t_type(); } else { eff_char16_t = integer_type((an_integer_kind)targ_char16_t_int_kind); } /* if */ return eff_char16_t; } /* eff_char16_t_type */ a_type_ptr eff_char32_t_type(void) /* Return the effective type of char32_t. This is a distinct type if char16_t_and_char32_t_are_keywords is TRUE, and the appropriate integral type otherwise. */ { a_type_ptr eff_char32_t; if (char16_t_and_char32_t_are_keywords) { eff_char32_t = char32_t_type(); } else { eff_char32_t = integer_type((an_integer_kind)targ_char32_t_int_kind); } /* if */ return eff_char32_t; } /* eff_char32_t_type */ a_type_ptr bool_type(void) /* Make or find a type entry for a bool type and return a pointer to it. */ { a_type_ptr pit; if (il_bool_type != NULL) { /* The type has previously been created, and can be reused. */ pit = il_bool_type; } else { /* The type must be created. */ il_bool_type = pit = alloc_type((a_type_kind)tk_integer); pit->variant.integer.int_kind = BOOL_INT_KIND; pit->variant.integer.bool_type = TRUE; set_type_size(pit); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)pit, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(pit); } /* if */ return pit; } /* bool_type */ a_type_ptr managed_nullptr_type(void) /* Make or find a type entry for the type of the C++/CLI nullptr keyword (which is different from std::nullptr_t and is an incomplete type) and return a pointer to it. */ { check_assertion(cli_or_cx_enabled); if (il_managed_nullptr_type == NULL) { /* The type must be created. */ il_managed_nullptr_type = alloc_type((a_type_kind)tk_nullptr); il_managed_nullptr_type->incomplete = TRUE; #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)il_managed_nullptr_type, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(il_managed_nullptr_type); } /* if */ return il_managed_nullptr_type; } /* managed_nullptr_type */ a_type_ptr standard_nullptr_type(void) /* Make or find a type entry for std::nullptr_t, the type of the nullptr keyword (and also the C++/CLI __nullptr keyword), and return a pointer to it. */ { if (il_standard_nullptr_type == NULL) { /* The type must be created. */ il_standard_nullptr_type = alloc_type((a_type_kind)tk_nullptr); set_type_size(il_standard_nullptr_type); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)il_standard_nullptr_type, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(il_standard_nullptr_type); } /* if */ return il_standard_nullptr_type; } /* standard_nullptr_type */ #if FIXED_POINT_ALLOWED a_boolean fixed_point_type_used_in_primary_IL( a_fixed_point_type_descr descr/*lint !e1746*/) /* Return TRUE if the fixed-point type with the indicated description was used in the primary IL so far. This routine should be called to determine if a fixed_point type should be lowered. */ { a_type_ptr *array_entry = &fixed_point_types[descr.precision] [(int)descr.is_unsigned] [(int)descr.is_fract_type] [(int)descr.saturating]; check_assertion(is_primary_translation_unit); return *array_entry != NULL; } /* fixed_point_type_used_in_primary_IL */ a_type_ptr fixed_point_type(a_fixed_point_type_descr descr/*lint !e1746*/) /* Make or find a fixed-point type entry with the given attributes and return a pointer to it. */ { a_type_ptr *p_result = &fixed_point_types[descr.precision] [(int)descr.is_unsigned] [(int)descr.is_fract_type] [(int)descr.saturating]; if (*p_result == NULL) { /* The type hasn't been created yet: Do so now. */ *p_result = alloc_type((a_type_kind)tk_fixed_point); (*p_result)->variant.fixed_point = descr; set_type_size(*p_result); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)(*p_result), (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(*p_result); } /* if */ return *p_result; } /* fixed_point_type */ #endif /* FIXED_POINT_ALLOWED */ a_type_ptr float_type(a_float_kind kind) /* Make or find a type entry for a float type of the indicated kind, and return a pointer to it. */ { a_type_ptr pft; if (float_types[kind] != NULL) { /* The type has previously been created, and can be reused. */ pft = float_types[kind]; } else { /* The type must be created. */ float_types[kind] = pft = alloc_type((a_type_kind)tk_float); pft->variant.float_kind = kind; set_type_size(pft); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)pft, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(pft); } /* if */ return pft; } /* float_type */ #if C99_IL_EXTENSIONS_SUPPORTED #if DO_IL_LOWERING a_boolean complex_type_used_in_primary_IL(a_float_kind kind) /* Return TRUE if the complex type of the indicated kind was used in the primary IL so far. This routine should be called to determine if a complex type should be lowered. */ { check_assertion(is_primary_translation_unit); return complex_types[kind] != NULL; } /* complex_type_used_in_primary_IL */ #endif /* DO_IL_LOWERING */ a_type_ptr complex_type(a_float_kind kind) /* Make or find a type entry for a complex type of the indicated kind, and return a pointer to it. */ { a_type_ptr pft; if (complex_types[kind] != NULL) { /* The type has previously been created, and can be reused. */ pft = complex_types[kind]; } else { /* The type must be created. */ complex_types[kind] = pft = alloc_type((a_type_kind)tk_complex); pft->variant.float_kind = kind; set_type_size(pft); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)pft, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(pft); } /* if */ return pft; } /* complex_type */ #if LOWER_COMPLEX a_boolean imaginary_type_used_in_primary_IL(a_float_kind kind) /* Return TRUE if the imaginary type of the indicated kind was used in the primary IL so far. This routine should be called to determine if an imaginary type should be lowered. */ { check_assertion(is_primary_translation_unit); return imaginary_types[kind] != NULL; } /* imaginary_type_used_in_primary_IL */ #endif /* LOWER_COMPLEX */ a_type_ptr imaginary_type(a_float_kind kind) /* Make or find a type entry for a imaginary type of the indicated kind, and return a pointer to it. */ { a_type_ptr pft; if (imaginary_types[kind] != NULL) { /* The type has previously been created, and can be reused. */ pft = imaginary_types[kind]; } else { /* The type must be created. */ imaginary_types[kind] = pft = alloc_type((a_type_kind)tk_imaginary); pft->variant.float_kind = kind; set_type_size(pft); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)pft, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(pft); } /* if */ return pft; } /* imaginary_type */ void set_complex_constant(a_float_kind float_kind, a_const_char *real, a_const_char *imag, a_constant *con) /* Fill in the constant "con" as a complex constant of kind "float_kind" with "real" and "imag" components. Should only be used with floating point strings that are known to be representable. */ { an_internal_float_value float_value; a_boolean err; clear_constant(con, (a_constant_repr_kind)ck_complex); con->type = complex_type(float_kind); fp_string_to_float(float_kind, real, &float_value, &err); check_assertion(!err); con->variant.complex_value->real = float_value; fp_string_to_float(float_kind, imag, &float_value, &err); check_assertion(!err); con->variant.complex_value->imag = float_value; } /* set_complex_constant */ #endif /* C99_IL_EXTENSIONS_SUPPORTED */ a_type_ptr string_literal_type(a_character_kind kind, a_targ_size_t num_chars) /* Make or find an entry for the type of a string literal with the given number of characters and the given character kind. Return a pointer to this type. Multiple calls with the same value of num_chars return the same type entry if num_chars <= MAX_TRACKED_STRING_TYPE_LENGTH and the calls occur in the front end proper (i.e., when in_front_end is TRUE). */ { a_type_ptr result; if (num_chars <= MAX_TRACKED_STRING_TYPE_LENGTH && in_front_end && string_types[kind][num_chars] != NULL) { /* The type has previously been created, and can be reused. */ result = string_types[kind][num_chars]; } else { a_type_ptr elem_type = character_type(kind); if (string_literals_are_const) { /* The element type is const (this is the case in standard C++, but was not the case in the C++ ARM). */ elem_type = make_qualified_type(elem_type, TQ_CONST); } /* if */ result = alloc_type((a_type_kind)tk_array); result->variant.array.element_type = elem_type; result->variant.array.variant.number_of_elements = num_chars; if (num_chars == 0) { result->variant.array.bound_is_zero = TRUE; } /* if */ set_type_size(result); if (num_chars <= MAX_TRACKED_STRING_TYPE_LENGTH && in_front_end) { string_types[kind][num_chars] = result; } /* if */ #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)result, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ } /* if */ return result; } /* string_literal_type */ a_type_ptr string_type(a_targ_size_t num_chars) /* Return the type of an ordinary character string of the given length. Note that this should not be implemented as a forwarding macro if the code is to be compiled as C++ because "string_type" is an identifier defined by the C++ standard library. */ { return string_literal_type((a_character_kind)chk_char, num_chars); } /* string_type */ a_type_ptr error_type(void) /* Make or find a type entry for an error type, and return a pointer to it. */ { if (il_error_type == NULL) { il_error_type = alloc_type((a_type_kind)tk_error); set_type_size(il_error_type); /* The type is deliberately not recorded as an orphan. If it were, it would be found by IL lowering. */ } /* if */ return il_error_type; } /* error_type */ a_type_ptr unknown_type(void) /* Make or find a type entry for an unknown type, and return a pointer to it. Such a type is only used in the front end; it does not survive into the back end. */ { if (il_unknown_type == NULL) { il_unknown_type = alloc_type((a_type_kind)tk_unknown); set_type_size(il_unknown_type); /* Deliberately not recorded as an orphan. */ } /* if */ return il_unknown_type; } /* unknown_type */ a_type_ptr void_type(void) /* Make or find a type entry for a void type, and return a pointer to it. */ { if (il_void_type == NULL) { /* alloc_type sets the incomplete flag to TRUE for tk_void. */ il_void_type = alloc_type((a_type_kind)tk_void); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)il_void_type, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(il_void_type); } /* if */ return il_void_type; } /* void_type */ STATIC_THREAD a_type_ptr il_strong_ordering_type, il_weak_ordering_type, il_partial_ordering_type, il_strong_equality_type, il_weak_equality_type; /* Pointers to the standard comparison category types. (See N4810 [cmp.categories].) */ static a_type_ptr get_ordering_type(a_const_char *name) /* Look up the given name in namespace std. If it is an (unqualified) enumeration type, return it. Otherwise issue an error (suggesting was not included) and return an error type. name should normally be one of: "strong_ordering", "weak_ordering", "partial_ordering", "strong_equality", or "weak_equality". */ { a_symbol_ptr sym = look_up_name_string_in_std(name); a_type_ptr result; if (sym == NULL || !is_type_symbol(sym) || is_enum_symbol(sym)) { expr_pos_st_error(ec_bad_ordering_type, &error_position, name); result = error_type(); } else { result = type_symbol_type(sym); if (is_qualified_type(result)) { result = error_type(); expr_pos_st_error(ec_bad_ordering_type, &error_position, name); } /* if */ } /* if */ return result; } /* get_ordering_type */ a_type_ptr strong_ordering_type(void) /* Return a type entry for std::strong_ordering (issue an error if on the first call to this routine std::strong_ordering is not appropriately declared). */ { if (il_strong_ordering_type == NULL) { il_strong_ordering_type = get_ordering_type("strong_ordering"); } /* if */ return il_strong_ordering_type; } /* strong_ordering_type */ a_type_ptr weak_ordering_type(void) /* Return a type entry for std::weak_ordering (issue an error if on the first call to this routine std::weak_ordering is not appropriately declared). */ { if (il_weak_ordering_type == NULL) { il_weak_ordering_type = get_ordering_type("weak_ordering"); } /* if */ return il_weak_ordering_type; } /* weak_ordering_type */ a_type_ptr partial_ordering_type(void) /* Return a type entry for std::partial_ordering (issue an error if on the first call to this routine std::partial_ordering is not appropriately declared). */ { if (il_partial_ordering_type == NULL) { il_partial_ordering_type = get_ordering_type("partial_ordering"); } /* if */ return il_partial_ordering_type; } /* partial_ordering_type */ a_type_ptr strong_equality_type(void) /* Return a type entry for std::strong_equality (issue an error if on the first call to this routine std::strong_equality is not appropriately declared). */ { if (il_strong_equality_type == NULL) { il_strong_equality_type = get_ordering_type("strong_equality"); } /* if */ return il_strong_equality_type; } /* strong_equality_type */ a_type_ptr weak_equality_type(void) /* Return a type entry for std::weak_equality (issue an error if on the first call to this routine std::weak_equality is not appropriately declared). */ { if (il_weak_equality_type == NULL) { il_weak_equality_type = get_ordering_type("weak_equality"); } /* if */ return il_weak_equality_type; } /* weak_equality_type */ a_type_ptr reflection_type(void) /* Make or find a type entry for a reflection type, and return a pointer to it. */ { if (il_reflection_type == NULL) { il_reflection_type = alloc_type(tk_reflection); set_type_size(il_reflection_type); #if ORPHAN_PROCESSING_NEEDED /* Record the type entry as an orphan in case it is discarded now and then found again in a later phase (e.g., IL lowering). */ add_orphaned_file_scope_il_entry((char *)il_reflection_type, (an_il_entry_kind)iek_type); #endif /* ORPHAN_PROCESSING_NEEDED */ record_builtin_type(il_reflection_type); } /* if */ return il_reflection_type; } /* reflection_type */ a_boolean check_consistent_string_view_type(a_type_ptr svtp) /* The given type is expected to be std::string_view. Ensure it has some of the expected characteristics of that type (e.g., being a member of std and being a class type), and, most importantly that it is consistent with any previous types that were expected to be std::string_view. */ { a_boolean result = TRUE; a_namespace_ptr std_nsp; svtp = skip_typerefs(svtp); if (svtp == il_std_string_view) { goto done; } else if (il_std_string_view != NULL) { pos_error(ec_inconsistent_std_string_view, &error_position); result = FALSE; goto done; } /* if */ std_nsp = symbol_for_namespace_std->variant.namespace_info.ptr; if (!is_namespace_member(svtp) || parent_namespace_of(svtp) != std_nsp || !is_immediate_class_type(svtp)) { result = FALSE; } else { a_const_char *name = unmangled_name_of(&svtp->source_corresp); if (name == NULL || strcmp(name, "basic_string_view") != 0) { result = FALSE; } /* if */ } /* if */ if (!result) { pos_ty_error(ec_bad_std_string_view, &error_position, svtp); goto done; } /* if */ il_std_string_view = svtp; done: return result; } /* check_consistent_string_view_type */ namespace { /* A structure bundling the field set for the GNU libstdc++ standard library's std::source_location::__impl type. */ struct a_gnu_source_location_field_set { a_field_ptr file; /* The __impl type's field representing the file name. */ a_field_ptr function; /* The __impl type's field representing the function name. */ a_field_ptr line; /* The __impl type's field representing the line number. */ a_field_ptr column; /* The __impl type's field representing the column number. */ }; /* a_gnu_source_location_field_set */ } /* namespace */ STATIC_THREAD a_type_ptr il_source_location_impl_type; /* A pointer to the GNU libstdc++ standard library's std::source_location::__impl type. */ STATIC_THREAD a_gnu_source_location_field_set il_source_location_fields; /* A collection of cached pointers to the fields of the GNU libstdc++ standard library's std::source_location::__impl type. */ static a_boolean map_to_gnu_source_location_field( a_field_ptr input, a_type_ptr cstr_ty, a_gnu_source_location_field_set *fields) /* Given a field to examine and the IL instance of the const char pointer (c-string) type, attempt to update the given field cache (fields). Return TRUE if a field matches and was mapped to one of the currently unknown fields in the field cache; otherwise, return FALSE. */ { a_boolean mapped = FALSE; a_const_char *name = input->source_corresp.name; if (strcmp(name, "_M_function_name") == 0) { if (fields->function == NULL && input->type == cstr_ty) { fields->function = input; mapped = TRUE; } /* if */ } else if (strcmp(name, "_M_file_name") == 0) { if (fields->file == NULL && input->type == cstr_ty) { fields->file = input; mapped = TRUE; } /* if */ } else if (strcmp(name, "_M_column") == 0) { if (fields->column == NULL && is_integral_type(input->type)) { fields->column = input; mapped = TRUE; } /* if */ } else if (strcmp(name, "_M_line") == 0) { if (fields->line == NULL && is_integral_type(input->type)) { fields->line = input; mapped = TRUE; } /* if */ } /* if */ return mapped; } /* map_to_gnu_source_location_field */ static a_boolean check_gnu_source_location_impl_type( a_type_ptr impl_type, a_gnu_source_location_field_set *fields) /* Given a potential GNU libstdc++ std::source_location::__impl type, validate compatibility. Additionally, map processed fields to the field info cache (fields). Return TRUE if the type is compatible, FALSE otherwise. */ { a_boolean valid = TRUE; a_class_type_supplement_ptr ctsp = class_type_supp(impl_type); a_base_class_ptr bcp = ctsp->base_classes; a_variable_ptr vp = ctsp->assoc_scope->variables; a_routine_ptr rp = ctsp->assoc_scope->routines; auto is_user_declared_routine = [](a_routine_ptr el) -> a_boolean { return !el->compiler_generated; }; if (count_list_elements(bcp) > 0) { /* Check to make sure there are no bases. */ valid = FALSE; } else if (count_list_elements(vp) > 0) { /* Check to make sure there are no static data members. */ valid = FALSE; } else if (count_list_elements(rp, is_user_declared_routine) > 0) { /* Check to make sure there are no user declared functions. */ valid = FALSE; } else { /* Check to make sure only the four expected fields are present. */ a_field_ptr field_1 = fields_of(impl_type); a_field_ptr field_2 = field_1 == NULL ? NULL : field_1->next; a_field_ptr field_3 = field_2 == NULL ? NULL : field_2->next; a_field_ptr field_4 = field_3 == NULL ? NULL : field_3->next; /* Retrieve the cstring type to match "string" fields against. */ a_type_ptr cstr_ty = make_pointer_type(make_qualified_type( integer_type((an_integer_kind)ik_char), TQ_CONST)); /* Map the fields into the field cache. As each mapping operation returns TRUE only when an previously unknown field becomes mapped, this both updates the fields cache and validates the fields. */ if (field_1 == NULL || !map_to_gnu_source_location_field(field_1, cstr_ty, fields)) { /* Check that field #1 is present, and maps to one of the four missing fields. */ valid = FALSE; } else if (field_2 == NULL || !map_to_gnu_source_location_field(field_2, cstr_ty, fields)) { /* Check that field #2 is present, and maps to one of the three missing fields. */ valid = FALSE; } else if (field_3 == NULL || !map_to_gnu_source_location_field(field_3, cstr_ty, fields)) { /* Check that field #3 is present, and maps to one of the two missing fields. */ valid = FALSE; } else if (field_4 == NULL || !map_to_gnu_source_location_field(field_4, cstr_ty, fields)) { /* Check that field #4 is present, and maps to the missing field. */ valid = FALSE; } else if (field_4->next != NULL) { /* Check that there is no field #5. */ valid = FALSE; } /* if */ } /* if */ return valid; } /* check_gnu_source_location_impl_type */ a_type_ptr gnu_source_location_impl_type(void) /* Attempt to look up and return a compatible GNU libstdc++ implementation type std::source_location::__impl. If said type cannot be found, issue a diagnostic and return an error type. Additionally, upon successful resolution of the type, update cached field information. */ { if (il_source_location_impl_type == NULL) { /* Attempt to resolve the libstdc++ "source_location" type within the "std" namespace. */ a_symbol_ptr class_sym = look_up_name_string_in_std("source_location"); a_type_ptr class_type = NULL; if (class_sym != NULL && is_class_symbol(class_sym)) { class_type = type_symbol_type(class_sym); } /* if */ /* Attempt to resolve the libstdc++ "__impl" type within the "source_location" class. */ if (class_type != NULL) { a_symbol_ptr impl_sym = look_up_name_string_in_class("__impl", class_type, IDL_NO_OPTIONS); if (impl_sym != NULL && is_class_symbol(impl_sym)) { a_type_ptr impl_type = type_symbol_type(impl_sym); /* Check that the implementation type matches expectations, and update the fields cache. If validation succeeds, use this type as the "__impl" type. */ if (check_gnu_source_location_impl_type(impl_type, &il_source_location_fields)) { il_source_location_impl_type = impl_type; } /* if */ } /* if */ } /* if */ /* If the type lookup or validation failed, diagnose and replace the type with an error type so further calls don't repeat this process. */ if (il_source_location_impl_type == NULL) { pos_error(ec_missing_gnu_srcloc_type, &error_position); il_source_location_impl_type = error_type(); } /* if */ } /* if */ return il_source_location_impl_type; } /* gnu_source_location_impl_type */ a_boolean has_gnu_source_location_impl_type(void) /* Return TRUE if a compatible GNU libstdc++ implementation type std::source_location::__impl has previously been found; otherwise, return FALSE. */ { return il_source_location_impl_type != NULL && !is_error_type(il_source_location_impl_type); } /* has_gnu_source_location_impl_type */ a_gnu_source_location_type_info gnu_source_location_impl(void) /* Return the aggregate type and field cache information. This function should only be called in contexts where it's known that a compatible GNU libstdc++ implementation type std::source_location::__impl has previously been found. This function is used by the interpreter and all information is bundled to reduce the number of function calls required during source_location interpretation. */ { check_assertion(il_source_location_impl_type != NULL); return a_gnu_source_location_type_info{il_source_location_impl_type, il_source_location_fields.file, il_source_location_fields.function, il_source_location_fields.line, il_source_location_fields.column}; } /* gnu_source_location_impl */ static a_constant_ptr get_constexpr_member_value(a_type_ptr class_type, a_const_char *name) /* Look up the given name in the given class type. It should find a constexpr static data member. Return the constant that initializes that member. Issue a hard error in case of failure since it indicates something is wrong with the header. */ { a_symbol_ptr sym; a_variable_ptr vp = NULL; a_constant_ptr cp = NULL; if (!is_class_struct_union_type(class_type)) { /* This can happen if class_type is an error type. */ expect_error(); cp = alloc_error_constant(); goto done; } /* if */ sym = look_up_name_string_in_class(name, class_type, IDL_DO_NOT_ADD_TO_NONREAL_CLASS); if (sym != NULL && symbol_is(sym, sk_static_data_member)) { vp = sym->variant.static_data_member.variable; } /* if */ if (vp != NULL && vp->init_kind == (an_init_kind)initk_static && is_const_qualified_type(vp->type)) { cp = vp->initializer.constant; } /* if */ if (cp == NULL) { pos_ty_str_error(ec_invalid_std_comparison_value, &error_position, class_type, name); cp = alloc_error_constant(); } /* if */ done: return cp; } /* get_constexpr_member_value */ void initialize_ordering_constants(void) /* Initialize strong_ordering_equal, etc. These are the constants initializing std::strong_ordering::equal, etc. */ { if (strong_ordering_equal == NULL) { strong_ordering_equal = get_constexpr_member_value( strong_ordering_type(), "equal"); strong_ordering_less = get_constexpr_member_value( strong_ordering_type(), "less"); strong_ordering_greater = get_constexpr_member_value( strong_ordering_type(), "greater"); weak_ordering_equivalent = get_constexpr_member_value( weak_ordering_type(), "equivalent"); weak_ordering_less = get_constexpr_member_value( weak_ordering_type(), "less"); weak_ordering_greater = get_constexpr_member_value( weak_ordering_type(), "greater"); partial_ordering_equivalent = get_constexpr_member_value( partial_ordering_type(), "equivalent"); partial_ordering_less = get_constexpr_member_value( partial_ordering_type(), "less"); partial_ordering_greater = get_constexpr_member_value( partial_ordering_type(), "greater"); partial_ordering_unordered = get_constexpr_member_value( partial_ordering_type(), "unordered"); if (ms_version_is(<1925)) { strong_equality_equal = get_constexpr_member_value( strong_equality_type(), "equal"); strong_equality_nonequal = get_constexpr_member_value( strong_equality_type(), "nonequal"); weak_equality_equivalent = get_constexpr_member_value( weak_equality_type(), "equivalent"); weak_equality_nonequivalent = get_constexpr_member_value( weak_equality_type(), "nonequivalent"); } /* if */ } /* if */ } /* initialize_ordering_constants */ #if DEBUG void db_based_types(a_type_ptr tp) /* Print the "based types" associated with the given type to the debug output. */ { a_based_type_list_member_ptr btlmp; for (btlmp = tp->based_types; btlmp != NULL; btlmp = btlmp->next) { fprintf(f_debug, "Based type (kind = %d) ", btlmp->kind); db_type(btlmp->based_type); fprintf(f_debug, "\n"); } /* for */ } /* db_based_types */ #endif /* DEBUG */ void do_based_type_fixup(void) /* Go though the based-type fixup list. For each type specified, go through the associated based-type list and remove any entries marked as front-end only. */ { a_based_type_fixup_ptr btfp = curr_translation_unit ->based_type_fixup_list; a_type_ptr tp; a_based_type_list_member_ptr btlmp, prev_btlmp, next_btlmp; /* Traverse the based-type fixup list. */ for (; btfp != NULL; btfp = btfp->next) { /* For each type on the list, traverse the associated based-type list and look for based-type entries marked front-end-only. */ tp = btfp->base_type; prev_btlmp = NULL; for (btlmp = tp->based_types; btlmp != NULL; btlmp = next_btlmp) { next_btlmp = btlmp->next; if (btlmp->front_end_only) { /* Match -- link around the entry and continue looping. */ if (prev_btlmp == NULL) { tp->based_types = btlmp->next; } else { prev_btlmp->next = btlmp->next; } /* if */ /* Leave prev_btlmp set as is. */ } else { prev_btlmp = btlmp; } /* if */ } /* for */ } /* for */ curr_translation_unit->based_type_fixup_list = NULL; } /* do_based_type_fixup */ static void add_to_based_type_fixup_list(a_type_ptr base_type, a_translation_unit_ptr trans_unit) /* Look for an entry on the based-type fixup list (of the given translation unit) that points to base_type. If none is found, create one and add it to the list. */ { a_based_type_fixup_ptr btfp, prev_btfp; if (!prototype_instantiations_in_il && is_template_dependent_type(base_type)) { /* Don't bother adding a fixup entry for base types that shouldn't escape the front end anyway. In other words, if T is a template param type, we don't need a fixup entry for T A::* but we do need one for int A::* (in which examples the base-types are T and int, respectively), because the base_type T is not going to be in the IL, but the base type int is. */ } else { prev_btfp = NULL; for (btfp = trans_unit->based_type_fixup_list; btfp != NULL; btfp = btfp->next) { if (btfp->base_type == base_type) { /* The specified base type is already represented on the fixup list. Unless it's already there, move the entry to the head of the list (as an optimization for subsequent traversals of the list). */ if (prev_btfp != NULL) { /* Remove the entry and re-add it at the front. */ prev_btfp->next = btfp->next; btfp->next = trans_unit->based_type_fixup_list; trans_unit->based_type_fixup_list = btfp; } /* if */ goto done; } /* if */ prev_btfp = btfp; } /* for */ /* Falling through means a match was not found. */ btfp = (a_based_type_fixup_ptr)alloc_fe(sizeof(a_based_type_fixup)); #if DEBUG num_based_type_fixups_allocated++; #endif /* DEBUG */ btfp->base_type = base_type; /* Add the entry to the start of the list. */ btfp->next = trans_unit->based_type_fixup_list; trans_unit->based_type_fixup_list = btfp; } /* if */ done:; } /* add_to_based_type_fixup_list */ static a_type_ptr get_based_type( a_type_ptr base_type, a_based_type_kind kind, a_type_qualifier_set qualifiers, ARG_UNUSED a_pointer_modifier_set modifiers, ARG_UNUSED a_boolean expl_mem_attr_implicit, a_type_ptr class_type, ARG_UNUSED a_upc_block_size upc_block_size) /* Search the based_types list of base_type to see if it contains a based type of the kind indicated by "kind". If the kind is "btk_qualified", the "qualifiers" parameter must match the "qualifiers" field of the based type, the upc_block_size parameter must match the underlying block size of the type (if it is UPC shared), and expl_mem_attr_implicit must match the explicit_memory_attribute_made_implicit flag of the based type (that's used for memory attributes like near/far). If the kind is "btk_ptr_to_member", the specified "class_type" must also match "class_of_which_a_member" of that based type. If kind is "btk_ptr_to_member" or "btk_pointer", the based type must have the pointer modifiers specified by "modifiers". Return a pointer to the type if such an entry exists, or NULL if no such entry exists. The based_types list is used to hold pointers to types based on the base type, so that only one copy of pointer-to that type, reference-to that type, etc., is allocated. As a simple optimization to based type lookup, move the desired based type, if found, to the front of the list. This will tend to keep frequently-asked-for based types at the front of the list. */ { a_type_ptr tp = NULL; a_based_type_list_member_ptr btlmp, prev_btlmp; a_boolean ptr_to_member; #if MICROSOFT_EXTENSIONS_ALLOWED a_boolean ptr; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if DEBUG num_get_based_type_calls++; #endif /* DEBUG */ #if MICROSOFT_EXTENSIONS_ALLOWED ptr = (kind == (a_based_type_kind)btk_pointer); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ ptr_to_member = (kind == (a_based_type_kind)btk_ptr_to_member); /* Search the based_types list looking for an entry of the right kind. */ for (btlmp = base_type->based_types, prev_btlmp = NULL; btlmp != NULL; prev_btlmp = btlmp, btlmp = btlmp->next) { if (btlmp->kind == kind) { tp = btlmp->based_type; if (ptr_to_member && pm_orig_class_type(tp) != class_type) { /* Pointer-to-member parent class does not match class type -- keep looking. */ tp = NULL; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (ptr_to_member && tp->variant.ptr_to_member.modifiers != modifiers) { tp = NULL; } else if (ptr && tp->variant.pointer.modifiers != modifiers) { tp = NULL; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else if (kind == (a_based_type_kind)btk_qualified && (tp->variant.typeref.qualifiers != qualifiers #if NEAR_AND_FAR_ALLOWED || (a_boolean)tp->variant.typeref. explicit_memory_attribute_made_implicit != expl_mem_attr_implicit #endif /* NEAR_AND_FAR_ALLOWED */ #if UPC_EXTENSIONS_ALLOWED || tp->variant.typeref.extra_info->upc_block_size != upc_block_size #endif /* UPC_EXTENSIONS_ALLOWED */ )) { /* Qualifiers do not match -- keep looking. */ tp = NULL; } else { /* This is a match. */ if (prev_btlmp != NULL) { /* There is at least one base type list member preceding this one; move the current based type member to the front of the list. */ prev_btlmp->next = btlmp->next; btlmp->next = base_type->based_types; base_type->based_types = btlmp; } /* if */ break; } /* if */ } /* if */ } /* for */ return tp; } /* get_based_type */ static void add_based_type_list_member(a_type_ptr base_type, a_based_type_kind kind, a_type_ptr based_type) /* Add a based type list member to the based_types list of base_type indicating that based_type is a type based on base_type, and the relationship between the types is described by kind. The entry is allocated in the file scope memory region. This routine does not check to see if there is already an entry of the indicated kind on the list. */ { a_based_type_list_member_ptr btlmp; btlmp = alloc_based_type_list_member(kind, base_type); btlmp->based_type = based_type; /* Add the entry to the front of the existing based_types list. */ btlmp->next = base_type->based_types; base_type->based_types = btlmp; if (!prototype_instantiations_in_il && btlmp->kind == (a_based_type_kind)btk_ptr_to_member) { a_type_ptr tp = based_type->variant.ptr_to_member.class_of_which_a_member; if (!is_class_struct_union_type(tp) || tp->variant.class_struct_union.is_nonreal_class) { /* Either the class specified in this ptr-to-member type is a template param type or a nonreal instantiation -- update the fixup list so this type doesn't leak into the back end. Pointers-to-members are odd, because the base type is the member type, so a template-dependent pointer to member type can be attached to a base type that is not template-dependent. The base type must stay in the IL, and the based type entry must not, so we arrange to remove the based type entry later in a fixup pass. */ btlmp->front_end_only = TRUE; add_to_based_type_fixup_list(base_type, curr_translation_unit); } /* if */ } /* if */ if (in_secondary_trans_unit(based_type) && !in_secondary_trans_unit(base_type)) { /* We're about to create a situation where a based-type entry in the primary IL points to a type in secondary IL. Record this in the based-type fixup list so that the entry can be removed later on. */ btlmp->front_end_only = TRUE; add_to_based_type_fixup_list(base_type, translation_units); } /* if */ #if MAINTAIN_NEEDED_FLAGS /* Don't set keep-in-il or needed flags on the based type if it is a front-end-only type. */ if (!btlmp->front_end_only) { /* If the "needed" or "keep_in_il" flag has already been set on this type, clear it and set it again to ensure that the entry on the based types list is visited if required. This is done by calling remark_as_needed/mark_to_keep_in_il for the primary entry, rather than for the based types list member, because the marking process ignores some based type entries and we don't want to duplicate the logic for that here. */ remark_as_needed((char *)base_type, iek_type); if (il_entry_prefix_of(base_type).keep_in_il) { il_entry_prefix_of(base_type).keep_in_il = FALSE; mark_to_keep_in_il((char *)base_type, iek_type); } /* if */ } /* if */ #endif /* MAINTAIN_NEEDED_FLAGS */ } /* add_based_type_list_member */ a_type_ptr make_partial_ptr_to_member_type(a_type_ptr class_type, a_type_ptr orig_class_type) /* Allocate and return a pointer-to-member type for members of the given class type. The type produced must eventually be "completed" by a call to update_ptr_to_member_type (which records the member type). Ordinarily, the given class_type cannot be a typeref, but in GNU and Microsoft modes, an exception is made to emulate a peculiar behavior in template instantiation contexts; a later call to update_ptr_to_member_type will replace any typerefs by the underlying class type entry in such cases. orig_class_type points to the class type as specified, including any typerefs. */ { a_type_ptr result = alloc_type((a_type_kind)tk_ptr_to_member); check_assertion(class_type != NULL); check_assertion(class_type->kind != (a_type_kind)tk_typeref || class_type->variant.typeref.is_dependent_type_operator || gpp_mode || microsoft_mode); result->variant.ptr_to_member.class_of_which_a_member = class_type; result->variant.ptr_to_member.orig_class_of_which_a_member = orig_class_type; return result; } /* make_partial_ptr_to_member_type */ static void adjust_component_types_of_ptr_to_member_type( a_type_ptr *p_class_type, a_type_ptr *p_member_type) /* *p_class_type and *p_member_type are about to be used to form a pointer to member type. Adjust *p_class_type as follows: - If *p_class_type is a template parameter type, replace it by the corresponding proxy class. - If *p_class_type is a typeref (only allowed in GNU and Microsoft modes), replace it by the underlying class type entry. and if *p_member_type is a function type, adjust it as follows: - Ensure its "this" class is set to the adjusted "*p_class_type". - Ensure its name linkage is "C++". - If the original *p_class_type was qualified (only allowed in GNU and Microsoft modes), apply those qualifiers to the function type (e.g, if the function type is "int C::() const" and the original *p_class_type is a "C volatile", set *p_member_type to "int C::() const volatile"). */ { a_type_ptr class_type = *p_class_type, member_type = *p_member_type; a_type_qualifier_set qualifiers = TQ_NONE; check_assertion(class_type != NULL && member_type != NULL); if (is_template_param_type(class_type) || (class_type->kind == (a_type_kind)tk_typeref && class_type->variant.typeref.is_dependent_type_operator)) { /* The class type is a template parameter. Substitute the template parameter's proxy class. */ class_type = proxy_class_for_template_param(class_type); *p_class_type = class_type; } else if (!is_immediate_class_type(class_type)) { check_assertion(gpp_mode || microsoft_mode); /* The qualifiers are ignored in Microsoft mode for parameters of partial instantiations of function templates. */ if (!microsoft_mode || !is_function_template_partial_instantiation_context()) { qualifiers = get_type_qualifiers(class_type); } /* if */ class_type = skip_typerefs(class_type); check_assertion(is_immediate_class_type(class_type)); *p_class_type = class_type; } /* if */ if (is_function_type(member_type)) { a_type_ptr ftype = skip_typerefs(member_type); a_routine_type_supplement_ptr rtsp = ftype->variant.routine.extra_info; if (rtsp->this_class == NULL || any_qualifier_in_set_missing(rtsp->qualifiers, qualifiers) || rtsp->routine_name_linkage == (a_name_linkage_kind)nlk_external) { /* Make a copy of the function type, in case it is shared. However, if the return type is not set, the type is still under construction and hence certainly not shared. */ if (ftype->variant.routine.return_type != NULL) { member_type = copy_routine_type_with_param_types( ftype, /*copy_default_args=*/TRUE); rtsp = member_type->variant.routine.extra_info; } /* if */ if (rtsp->this_class == NULL) { /* Ensure that the function type is a member function type. */ rtsp->this_class = skip_typerefs(class_type); rtsp->has_this_param = TRUE; } /* if */ if (qualifiers != TQ_NONE) { /* Merge in qualifiers in some Microsoft/GNU instantiation contexts. */ copy_qualifiers(rtsp->qualifiers | qualifiers, rtsp->qualifiers); } /* if */ if (rtsp->routine_name_linkage == (a_name_linkage_kind)nlk_external) { /* Change from C linkage to C++ linkage: */ rtsp->routine_name_linkage = (a_name_linkage_kind)nlk_cplusplus_external; } /* if */ /* Update the caller's type. */ *p_member_type = member_type; } /* if */ } /* if */ } /* adjust_component_types_of_ptr_to_member_type */ void update_ptr_to_member_type(a_type_ptr ptr_mem_type, a_type_ptr member_type) /* Set the member type recorded in the given pointer to member type to the given member type (after any needed adjustments if member type is a function type). The class type associated with the pointer to member type is also adjusted if needed to obtain a valid type entry (e.g., if it previously was a template parameter, the associated proxy class is used instead). */ { check_assertion(ptr_mem_type->kind == (a_type_kind)tk_ptr_to_member); adjust_component_types_of_ptr_to_member_type( &ptr_mem_type->variant.ptr_to_member.class_of_which_a_member, &member_type); ptr_mem_type->variant.ptr_to_member.type = member_type; } /* update_ptr_to_member_type */ a_type_ptr ptr_to_member_type_full(a_type_ptr member_type, a_type_ptr class_type, a_type_ptr orig_class_type, a_pointer_modifier_set modifiers) /* Return a pointer-to-member type, initializing its fields based on the specified member and class types. Attempt to find and reuse an existing type entry; otherwise a new entry is allocated. modifiers describes the pointer modifiers (like "__ptr32", a Microsoft extension) that are requested. member_type and class_type must be both be non-NULL and should usually not be a typeref; an exception are certain template instantiation cases in GNU and Microsoft modes (where any class type qualifiers may be transferred to a member function type). orig_class_type points to the class type as specified, including any typerefs. */ { a_type_ptr tp; a_based_type_kind kind = (a_based_type_kind)btk_ptr_to_member; adjust_component_types_of_ptr_to_member_type(&class_type, &member_type); check_assertion(!is_void_type(member_type) && !is_reference_type(member_type)); /* See if a pointer-to-member type such as the one being requested has already been allocated. If one was allocated, a pointer to it is stored in the based_types list of the member type, and the pointer can be reused. */ tp = get_based_type(member_type, kind, TQ_NONE, modifiers, /*expl_mem_attr_implicit=*/FALSE, orig_class_type, UPC_BLOCK_SIZE_NONE); if (tp == NULL) { /* No previously allocated entry: Allocate one. */ tp = alloc_type((a_type_kind)tk_ptr_to_member); tp->variant.ptr_to_member.type = member_type; tp->variant.ptr_to_member.class_of_which_a_member = class_type; tp->variant.ptr_to_member.orig_class_of_which_a_member = orig_class_type; #if MICROSOFT_EXTENSIONS_ALLOWED tp->variant.ptr_to_member.modifiers = modifiers; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ set_type_size(tp); add_based_type_list_member(member_type, kind, tp); #if MICROSOFT_EXTENSIONS_ALLOWED if (microsoft_mode) { /* A pointer-to-member declaration involving a given class type locks in the inheritance kind (i.e., the pointer-to-member representation). */ a_class_type_supplement_ptr ctsp = class_type_supp(class_type); /* Force instantiation of template class. */ instantiate_template_class(class_type); if (ctsp->inheritance_kind == (an_inheritance_kind)ihk_none) { ctsp->inheritance_kind = implied_inheritance_kind(class_type); } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ return tp; } /* ptr_to_member_type_full */ static a_type_ptr related_member_type(a_type_ptr member_type, a_type_ptr class_type) /* member_type is intended to be the member type under a pointer-to-member type for the class class_type. If member_type is a function type, check that the "this" parameter type matches class_type. If not, create a new function type with the right underlying class type and return it. In all other cases, return the original member type. */ { if (is_function_type(member_type)) { a_routine_type_supplement_ptr old_rtsp = skip_typerefs(member_type)->variant.routine.extra_info; a_type_ptr old_this_class = old_rtsp->this_class; /* A function type under a pointer-to-member must be a member function and therefore must have a "this" parameter type. */ check_assertion(old_this_class != NULL); if (old_this_class != class_type) { /* Make a new function type with the right "this" class. Note that there is no sharing of types going on here, so this may be wasteful if called a lot. */ a_type_ptr new_member_type; a_routine_type_supplement_ptr new_rtsp; new_member_type = copy_routine_type_with_param_types(member_type, /*copy_default_args=*/FALSE); new_rtsp = skip_typerefs(new_member_type)->variant.routine.extra_info; new_rtsp->this_class = class_type; new_rtsp->has_this_param = TRUE; member_type = new_member_type; } /* if */ } /* if */ return member_type; } /* related_member_type */ a_type_ptr related_ptr_to_member_type(a_type_ptr member_type, a_type_ptr class_type) /* Make a pointer-to-member type having the indicated member type and class type and return a pointer to it. If the member_type is a function type, alter the underlying "this" parameter type if necessary to be the new class_type. */ { a_type_ptr type; /* Make a new member type with an altered "this" parameter type, or leave the member type unchanged. */ member_type = related_member_type(member_type, class_type); /* Make the pointer-to-member type. */ type = ptr_to_member_type(member_type, class_type, class_type); return type; } /* related_ptr_to_member_type */ a_type_ptr function_type_without_noexcept_exception_spec(a_type_ptr type) /* Given a function or pointer-to-member-function type, if the associated exception specification is a noexcept type, return an equivalent type that has no exception specification, otherwise return the type itself. This is currently used only for typeinfo (where nothrow exception types are stripped and a typeinfo flag is added). */ { a_type_ptr ptr = type; if (!exc_spec_in_func_type || !exceptions_enabled) { /* There are no exception specifications in function types or exceptions are disabled; return the input. */ } else { /* See if the type has a noexcept exception specification. */ a_type_ptr base_type = skip_typerefs(type); if (is_nothrow_type(base_type)) { /* See if we've already created the requested type; if so, return it. */ ptr = get_based_type(base_type, (a_based_type_kind)btk_no_noexcept_exception_spec, TQ_NONE, PM_NONE, /*expl_mem_attr_implicit=*/FALSE, (a_type_ptr)NULL, UPC_BLOCK_SIZE_NONE); if (ptr == NULL) { /* Create a copy of the type without the exception specification. */ a_type_ptr orig_type = NULL; a_boolean is_ptr_to_member = FALSE; an_exception_specification_ptr save_esp; if (is_ptr_to_member_type(base_type)) { orig_type = base_type; base_type = pm_member_type(base_type); is_ptr_to_member = TRUE; } /* if */ check_assertion(is_function_type(base_type)); save_esp = base_type->variant.routine.extra_info->exception_specification; /* Create a copy of the function type without the exception specification (and ensure the lowering flag is set properly). */ base_type->variant.routine.extra_info->exception_specification = NULL; ptr = alloc_type(base_type->kind); copy_type(base_type, ptr); base_type->variant.routine.extra_info->exception_specification = save_esp; #if DO_IL_LOWERING il_lowering_flag_of(ptr) = visited_yet(type); ptr->typeinfo_var = NULL; #endif /* DO_IL_LOWERING */ if (is_ptr_to_member) { /* Copy the original (pointer-to-member) type and replace the member portion with the copied function type from above. */ a_type_ptr save_copied_type = ptr; ptr = alloc_type(orig_type->kind); copy_type(orig_type, ptr); ptr->variant.ptr_to_member.type = save_copied_type; base_type = orig_type; #if DO_IL_LOWERING il_lowering_flag_of(ptr) = visited_yet(type); ptr->typeinfo_var = NULL; #endif /* DO_IL_LOWERING */ } /* if */ /* Remember the existence of this type by putting a pointer to it in the based_types list. */ add_based_type_list_member(base_type, (a_based_type_kind)btk_no_noexcept_exception_spec, ptr); } /* if */ } /* if */ } /* if */ return ptr; } /* function_type_without_noexcept_exception_spec */ a_type_ptr make_pointer_type_full(a_type_ptr pointed_to_type, a_pointer_modifier_set modifiers) /* Allocate a pointer type record and initialize it. Attempt to find and reuse an existing entry if possible. modifiers describes the pointer modifiers (like "__ptr32", a Microsoft extension) that are requested. (pointed_to_type may be NULL, e.g., when building a type from nested declarators outward.) */ { a_type_ptr ptr; a_based_type_kind kind = (a_based_type_kind)btk_pointer; /* See if a pointer type for the type pointed to has already been allocated. If one was allocated, a pointer to it is stored in the based_types list for the base type, and the pointer type can be reused. */ if (pointed_to_type != NULL) { ptr = get_based_type(pointed_to_type, kind, TQ_NONE, modifiers, /*expl_mem_attr_implicit=*/FALSE, /*class_type=*/(a_type_ptr)NULL, UPC_BLOCK_SIZE_NONE); } else { ptr = NULL; } /* if */ if (ptr == NULL) { /* No allocated entry, need to allocate one. */ ptr = alloc_type((a_type_kind)tk_pointer); ptr->variant.pointer.type = pointed_to_type; #if MICROSOFT_EXTENSIONS_ALLOWED ptr->variant.pointer.modifiers = modifiers; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ set_type_size(ptr); if (pointed_to_type != NULL) { /* Remember the existence of this pointer type by putting a pointer to it in the based_types list. */ add_based_type_list_member(pointed_to_type, kind, ptr); } /* if */ } /* if */ return ptr; } /* make_pointer_type_full */ a_type_ptr make_reference_type(a_type_ptr pointed_to_type) /* Allocate an ordinary ("lvalue") reference type record and initialize it. Attempt to find and reuse an existing entry if possible. */ { a_type_ptr ptr; /* See if a reference type for the type pointed to has already been allocated. If one was allocated, a pointer to it is stored in the based_types list for the base type, and the reference type can be reused. */ ptr = get_based_type(pointed_to_type, (a_based_type_kind)btk_reference, TQ_NONE, PM_NONE, /*expl_mem_attr_implicit=*/FALSE, /*class_type=*/(a_type_ptr)NULL, UPC_BLOCK_SIZE_NONE); if (ptr == NULL) { /* No allocated entry, need to allocate one. */ ptr = alloc_type((a_type_kind)tk_pointer); ptr->variant.pointer.type = pointed_to_type; ptr->variant.pointer.is_reference = TRUE; set_type_size(ptr); /* Remember the existence of this reference type by putting a pointer to it in the based_types list. */ add_based_type_list_member(pointed_to_type, (a_based_type_kind)btk_reference, ptr); } /* if */ return ptr; } /* make_reference_type */ a_type_ptr make_rvalue_reference_type(a_type_ptr pointed_to_type) /* Allocate an rvalue reference type record and initialize it. Attempt to find and reuse an existing entry if possible. */ { a_type_ptr ptr; /* See if an rvalue reference type for the type pointed to has already been allocated. If one was allocated, a pointer to it is stored in the based_types list for the base type, and the reference type can be reused. */ ptr = get_based_type(pointed_to_type, (a_based_type_kind)btk_rvalue_reference, TQ_NONE, PM_NONE, /*expl_mem_attr_implicit=*/FALSE, /*class_type=*/(a_type_ptr)NULL, UPC_BLOCK_SIZE_NONE); if (ptr == NULL) { /* No allocated entry, need to allocate one. (Note that in GNU and Clang modes, rvalue references may be accepted with a warning even in C++03 modes that do not enable rvalue references. In such modes, rvalue references may also be generated implicitly, e.g., through range-based "for" statements. In other modes, that can happen as part of error recovery.) */ check_assertion_or_expect_error( rvalue_references_enabled || gpp_mode || clang_mode); ptr = alloc_type((a_type_kind)tk_pointer); ptr->variant.pointer.type = pointed_to_type; ptr->variant.pointer.is_reference = TRUE; ptr->variant.pointer.is_rvalue_reference = TRUE; set_type_size(ptr); /* Remember the existence of this reference type by putting a pointer to it in the based_types list. */ add_based_type_list_member(pointed_to_type, (a_based_type_kind)btk_rvalue_reference, ptr); } /* if */ return ptr; } /* make_rvalue_reference_type */ #if MICROSOFT_EXTENSIONS_ALLOWED a_type_ptr make_handle_type(a_type_ptr pointed_to_type) /* Allocate a C++/CLI handle type and initialize it. Attempt to find and reuse an existing entry if possible. */ { a_type_ptr ptr; /* Box built-in types and enum types when creating handles. */ if (is_boxable_type(pointed_to_type)) { ptr = boxed_type_for(pointed_to_type); pointed_to_type = type_plus_qualifiers_from_second_type(ptr, pointed_to_type); } /* if */ /* See if a handle type for the type pointed to has already been allocated. If one was allocated, a pointer to it is stored in the based_types list for the base type, and the handle type can be reused. */ ptr = get_based_type(pointed_to_type, (a_based_type_kind)btk_handle, TQ_NONE, PM_NONE, /*expl_mem_attr_implicit=*/FALSE, /*class_type=*/(a_type_ptr)NULL, UPC_BLOCK_SIZE_NONE); if (ptr == NULL) { /* No allocated entry, need to allocate one. */ ptr = alloc_type((a_type_kind)tk_pointer); ptr->variant.pointer.type = pointed_to_type; ptr->variant.pointer.is_reference = FALSE; ptr->variant.pointer.is_handle = TRUE; set_type_size(ptr); /* Remember the existence of this handle type by putting a pointer to it in the based_types list. */ add_based_type_list_member(pointed_to_type, (a_based_type_kind)btk_handle, ptr); } /* if */ return ptr; } /* make_handle_type */ a_type_ptr make_handle_to_system_string(void) /* Return the C++/CLI type "handle to System::String". */ { a_type_ptr string_handle = make_handle_type(type_symbol_type( cli_symbol_from_kind(csk_system_string))); return string_handle; } /* make_handle_to_system_string */ a_type_ptr make_tracking_reference_type(a_type_ptr pointed_to_type) /* Allocate a C++/CLI tracking reference type and initialize it. Attempt to find and reuse an existing entry if possible. */ { a_type_ptr ptr; /* See if a tracking reference type for the type pointed to has already been allocated. If one was allocated, a pointer to it is stored in the based_types list for the base type, and the tracking reference type can be reused. */ ptr = get_based_type(pointed_to_type, (a_based_type_kind)btk_tracking_ref, TQ_NONE, PM_NONE, /*expl_mem_attr_implicit=*/FALSE, /*class_type=*/(a_type_ptr)NULL, UPC_BLOCK_SIZE_NONE); if (ptr == NULL) { /* No allocated entry, need to allocate one. */ ptr = alloc_type((a_type_kind)tk_pointer); ptr->variant.pointer.type = pointed_to_type; ptr->variant.pointer.is_reference = TRUE; ptr->variant.pointer.is_handle = TRUE; set_type_size(ptr); /* Remember the existence of this tracking reference type by putting a pointer to it in the based_types list. */ add_based_type_list_member(pointed_to_type, (a_based_type_kind)btk_tracking_ref, ptr); } /* if */ return ptr; } /* make_tracking_reference_type */ a_type_ptr make_interior_ptr_type(a_type_ptr pointed_to_type) /* Allocate a C++/CLI interior_ptr type and initialize it. Attempt to find and reuse an existing entry if possible. */ { a_type_ptr ptr; check_assertion(cppcli_enabled); /* See if an interior_ptr type for the type pointed to has already been allocated. If one was allocated, a pointer to it is stored in the based_types list for the base type, and the interior_ptr type can be reused. */ ptr = get_based_type(pointed_to_type, (a_based_type_kind)btk_interior_ptr, TQ_NONE, PM_NONE, /*expl_mem_attr_implicit=*/FALSE, /*class_type=*/(a_type_ptr)NULL, UPC_BLOCK_SIZE_NONE); if (ptr == NULL) { /* No allocated entry, need to allocate one. */ ptr = alloc_type((a_type_kind)tk_pointer); ptr->variant.pointer.type = pointed_to_type; ptr->variant.pointer.is_interior_ptr = TRUE; set_type_size(ptr); /* Remember the existence of this interior_ptr type by putting a pointer to it in the based_types list. */ add_based_type_list_member(pointed_to_type, (a_based_type_kind)btk_interior_ptr, ptr); } /* if */ return ptr; } /* make_interior_ptr_type */ a_type_ptr make_pin_ptr_type(a_type_ptr pointed_to_type) /* Allocate a C++/CLI pin_ptr type and initialize it. Attempt to find and reuse an existing entry if possible. */ { a_type_ptr ptr; check_assertion(cppcli_enabled); /* See if a pin_ptr type for the type pointed to has already been allocated. If one was allocated, a pointer to it is stored in the based_types list for the base type, and the pin_ptr type can be reused. */ ptr = get_based_type(pointed_to_type, (a_based_type_kind)btk_pin_ptr, TQ_NONE, PM_NONE, /*expl_mem_attr_implicit=*/FALSE, /*class_type=*/(a_type_ptr)NULL, UPC_BLOCK_SIZE_NONE); if (ptr == NULL) { /* No allocated entry, need to allocate one. */ ptr = alloc_type((a_type_kind)tk_pointer); ptr->variant.pointer.type = pointed_to_type; ptr->variant.pointer.is_pin_ptr = TRUE; set_type_size(ptr); /* Remember the existence of this pin_ptr type by putting a pointer to it in the based_types list. */ add_based_type_list_member(pointed_to_type, (a_based_type_kind)btk_pin_ptr, ptr); } /* if */ return ptr; } /* make_pin_ptr_type */ a_type_ptr make_cppcx_box_type(a_type_ptr boxed_type) /* Allocate a C++/CX Platform::Box type and initialize it. Attempt to find and reuse an existing entry if possible. */ { a_type_ptr ptr; a_type_ptr unqualified_boxed_type = make_unqualified_type(boxed_type); /* See if a Platform::Box type for the type pointed to has already been allocated. If one was allocated, a pointer to it is stored in the based_types list for the base type, and the default::Box type can be reused. */ ptr = get_based_type(unqualified_boxed_type, (a_based_type_kind)btk_cppcx_box, TQ_NONE, PM_NONE, /*expl_mem_attr_implicit=*/FALSE, /*class_type=*/(a_type_ptr)NULL, UPC_BLOCK_SIZE_NONE); if (ptr == NULL) { /* No existing entry: Create a new one. */ a_symbol_ptr sym; a_template_arg_ptr arg_list; a_symbol_ptr box_template_sym = cli_symbol_from_kind(csk_cppcx_box); /* Build the argument list. */ arg_list = alloc_template_arg((a_templ_arg_kind)tak_type); arg_list->variant.type = unqualified_boxed_type; sym = find_template_class(box_template_sym, &arg_list, /*any_prototype_allowed=*/TRUE, /*specific_prototype_allowed=*/NULL, /*instantiate_nonreal=*/FALSE, /*do_not_create=*/FALSE, /*in_substitution=*/FALSE); free_template_arg_list(arg_list); ptr = type_symbol_type(sym); complete_type_is_needed(ptr); /* Remember the existence of this C++/CX box type by putting a pointer to it in the based_types list. */ add_based_type_list_member(unqualified_boxed_type, (a_based_type_kind)btk_cppcx_box, ptr); } /* if */ return ptr; } /* make_cppcx_box_type */ STATIC_THREAD a_routine_ptr idisposable_dispose_routine; a_routine_ptr get_idisposable_dispose_routine(void) /* Return the routine entry for "System::IDisposable::Dispose". */ { if (idisposable_dispose_routine == NULL) { a_symbol_locator locator; a_type_ptr system_idisposable_type = cli_class_type_for(csk_system_idisposable); a_symbol_ptr sym; complete_type_is_needed(system_idisposable_type); clear_locator(&locator, &system_idisposable_type->source_corresp.decl_position); (void)find_symbol("Dispose", sizeof("Dispose")-1, &locator); clear_specific_symbol(locator); sym = class_qualified_id_lookup(&locator, system_idisposable_type, IDL_DIRECT_CLASS_MEMBERS_ONLY); /* System::IDisposable::Dispose should be a member function with the following signature: public: virtual void Dispose(); */ if (sym != NULL && symbol_is(sym, sk_member_function)) { a_routine_ptr routine = sym->variant.routine.ptr; if (routine->source_corresp.access == (an_access_specifier)as_public && routine->is_virtual && is_void_type(routine->type->variant.routine.return_type) && function_type_params(routine->type) == NULL) { idisposable_dispose_routine = routine; } /* if */ } /* if */ if (idisposable_dispose_routine == NULL) { catastrophe(ec_invalid_idisposable_dispose); } /* if */ } /* if */ return idisposable_dispose_routine; } /* get_idisposable_dispose_routine */ STATIC_THREAD a_routine_ptr object_finalize_routine; a_routine_ptr get_object_finalize_routine(void) /* Return the symbol for "System::Object::Finalize". */ { if (object_finalize_routine == NULL) { a_boolean is_valid_object_finalize; a_type_ptr system_object_type = cli_system_object_type(); complete_type_is_needed(system_object_type); object_finalize_routine = find_finalize_routine(system_object_type, &is_valid_object_finalize); if (object_finalize_routine == NULL || !is_valid_object_finalize) { catastrophe(ec_invalid_object_finalize); } /* if */ } /* if */ return object_finalize_routine; } /* get_object_finalize_routine */ void f_set_clrcall_convention_if_needed(a_type_ptr rtp) /* If the given routine type has a component in its signature requiring the __clrcall calling convention, record that as the implicit calling convention. Issue an error if the function type is a vararg function type (because __clrcall doesn't permit varargs). */ { check_assertion(cppcli_enabled); check_assertion(rtp->kind == (a_type_kind)tk_routine); if (rtp->variant.routine.extra_info->calling_convention != (a_calling_convention)cc_clrcall && function_type_has_clrcall_component(rtp)) { /* Set the calling convention to __clrcall. */ if (rtp->variant.routine.extra_info->has_ellipsis) { /* __clrcall doesn't permit varargs. */ pos_error(ec_generic_parameter_does_not_permit_varargs, &error_position); } else if (rtp->variant.routine.extra_info->explicit_calling_convention) { /* If a (different) calling convention was specified explicitly, issue a warning. */ pos_warning(ec_generic_parameter_requires_clrcall, &error_position); } /* if */ rtp->variant.routine.extra_info->calling_convention = (a_calling_convention)cc_clrcall; } /* if */ } /* f_set_clrcall_convention_if_needed */ #if TEMPLATE_INSTANTIATIONS_IN_SOURCE_SEQUENCE_LISTS a_boolean f_is_member_of_namespace_cli(a_source_correspondence *scp) /* Return TRUE if and only if the given entity is a member of the C++/CLI namespace "cli". */ { a_boolean result = FALSE; if (scp_is_namespace_member(scp) && cli_or_cx_enabled) { check_assertion(cli_symbol_from_kind(csk_cli_namespace) != NULL); result = scp->parent_scope == cli_symbol_from_kind(csk_cli_namespace)-> variant.namespace_info.ptr->variant.assoc_scope; } /* if */ return result; } /* f_is_member_of_namespace_cli */ #endif /* TEMPLATE_INSTANTIATIONS_IN_SOURCE_SEQUENCE_LISTS */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ a_type_ptr make_pointer_type_of_same_kind( a_type_ptr base_type, ARG_UNUSED a_type_ptr model_pointer_type) /* Make a pointer type with base_type as the underlying type, and return it. Make the same kind of pointer as the kind indicated by model_pointer_type (this comes up in C++/CLI where interior_ptr, pin_ptr, and handles are pointer-like types). */ { a_type_ptr result_type; #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && is_interior_ptr_type(model_pointer_type)) { result_type = make_interior_ptr_type(base_type); } else if (cli_or_cx_enabled && is_pin_ptr_type(model_pointer_type)) { result_type = make_pin_ptr_type(base_type); } else if (cli_or_cx_enabled && is_handle_type(model_pointer_type)) { result_type = make_handle_type(base_type); } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here. */ { result_type = make_pointer_type(base_type); } /* if */ return result_type; } /* make_pointer_type_of_same_kind */ a_type_ptr make_reference_type_of_same_kind(a_type_ptr base_type, a_type_ptr model_ref_type) /* Make a reference type with base_type as the underlying type, and return it. Make the same kind of reference as the kind indicated by model_ref_type, e.g., an rvalue reference if model_ref_type is an rvalue reference. */ { a_type_ptr result_type; check_assertion(is_reference_type(model_ref_type)); if (is_rvalue_reference_type(model_ref_type)) { result_type = make_rvalue_reference_type(base_type); #if MICROSOFT_EXTENSIONS_ALLOWED } else if (cli_or_cx_enabled && is_tracking_reference_type(model_ref_type)) { result_type = make_tracking_reference_type(base_type); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else { result_type = make_reference_type(base_type); } /* if */ return result_type; } /* make_reference_type_of_same_kind */ a_type_ptr make_reference_to_reference(a_type_ptr base_ref_type, a_boolean rvalue_ref, ARG_UNUSED a_boolean tracking_ref, a_type_qualifier_set qualifiers, a_source_position *qual_pos, ARG_UNUSED a_boolean *is_error) /* Make a type "T cv1 &" (if rvalue_ref is FALSE), "T cv1 &&" (if rvalue_ref is TRUE), or "T cv1 %" (if tracking_ref is TRUE) where T is a reference type given by base_ref_type (after qualifiers -- like "restrict" -- on top of that type have been dropped) and cv1 are the given type qualifiers. (tracking_ref can be TRUE only in C++/CLI mode.) The resulting type is: - T if T is an lvalue reference or rvalue_ref is TRUE, - "X cv2&" where T is "X cv2&&", otherwise. If the cv1 and cv2 qualifiers represent conflicting "__near" and "__far" qualifications, an error type is returned *is_error is set to TRUE if is_error is non-NULL, and an error is issued if qual_pos is non-NULL. Otherwise, if qualifiers (i.e., cv1) is not TQ_NONE and qual_pos is non-NULL, a warning is issued (because cv1 is dropped from the result). An error case similarly occurs when tracking_ref is TRUE and the base_ref_type represents a standard (i.e., non-tracking) reference, or vice versa. All diagnostics are issued at the given source position. If is_error is NULL, qual_pos must be non-NULL. */ { a_type_ptr result; a_type_ptr under_ref = type_pointed_to(base_ref_type); #if NEAR_AND_FAR_ALLOWED a_type_qualifier_set base_qualifiers = get_type_qualifiers(under_ref); #endif /* NEAR_AND_FAR_ALLOWED */ check_assertion(qual_pos != NULL || is_error != NULL); #if NEAR_AND_FAR_ALLOWED if (((qualifiers | base_qualifiers) & (TQ_NEAR | TQ_FAR)) == (TQ_NEAR | TQ_FAR)) { /* Conflicting "near" and "far" qualification; this is an error. */ if (is_error == NULL) { pos_error(ec_mem_attrib_incompatible, qual_pos); } else { *is_error = TRUE; } /* if */ result = error_type(); } else #endif /* NEAR_AND_FAR_ALLOWED */ /* Do not insert code here. */ #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && skip_typerefs(base_ref_type)->variant.pointer.is_handle != tracking_ref) { /* Mixing tracking and non-tracking references is not allowed. */ if (is_error == NULL) { pos_error(ec_invalid_ref_tracking_ref_combination, &error_position); } else { *is_error = TRUE; } /* if */ result = error_type(); } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here. */ { if (qualifiers != TQ_NONE) { /* Some qualifiers are going to be ignored. Issue a warning if appropriate. */ if (depth_scope_stack == NO_SCOPE_DEPTH || (is_nonspecialized_instantiation_context() && !scope_stack[depth_scope_stack].in_prototype_instantiation)) { /* A real template instantiation: Such situations are often unavoidable in generic code, and therefore should not be warned about. */ } else if (qual_pos == NULL) { /* There is no position for the qualifiers. This happens during deduction, which is also a context where a warning would not be appropriate. */ } else if (qualifiers == TQ_RESTRICT) { pos_warning(ec_restrict_qualifier_ignored, qual_pos); } else { pos_warning(ec_type_qualifiers_ignored_on_reference, qual_pos); } /* if */ } /* if */ if (rvalue_ref || is_any_lvalue_reference_type(base_ref_type)) { /* The result should be base_ref_type, but without any top-level qualifiers (i.e., nonstandard qualifiers like "restrict"). Try to preserve a top-level typedef for nicer output in the C++-generating back end. */ result = skip_typerefs_not_typedefs(base_ref_type); if (get_type_qualifiers(result) != TQ_NONE) { base_ref_type = skip_typerefs(base_ref_type); } /* if */ } else { /* base_ref_type is an rvalue reference, but an lvalue reference must be returned. */ check_assertion(is_rvalue_reference_type(base_ref_type)); result = make_reference_type(under_ref); #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && is_tracking_reference_type(base_ref_type)) { result->variant.pointer.is_handle = TRUE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ #if DEBUG if (db_flag_is_set("ref_to_ref")) { fprintf(f_debug, "Ref-to-ref resulted in (line %lu): ", (unsigned long)pos_curr_token.seq); db_type(result); fprintf(f_debug, "\n"); } /* if */ #endif /* DEBUG */ } /* if */ return result; } /* make_reference_to_reference */ a_type_ptr copy_array_type_replacing_element_type(a_type_ptr old_array, a_type_ptr element_type) /* old_array is an array type that needs to be copied, but with a new element type (one that may, e.g., differ from the existing element type in its type qualifiers). Make the copy, allowing for multidimensional arrays, and return a pointer to the new array type. */ { a_type_ptr tp, new_array = NULL, prev = NULL; /* Loop, in case this is a multidimensional array. */ for (;;) { a_targ_alignment explicit_alignment = 0; /* Drop typedefs; there shouldn't be any other typerefs. Record any applicable explicit alignment along the way (so it can be applied on the copied type if needed). */ while (old_array->kind == (a_type_kind)tk_typeref) { if (old_array->alignment_set_explicitly && explicit_alignment == 0 && !(gnu_mode && gnu_version < 40000)) { explicit_alignment = old_array->alignment; } /* if */ old_array = old_array->variant.typeref.type; } /* while */ /* Allocate a new array type and copy the old one into it. Note that some of the source correspondence information should not be preserved in the copy. */ tp = alloc_type((a_type_kind)tk_array); copy_type(old_array, tp); break_source_corresp(&tp->source_corresp); if (explicit_alignment != 0) { /* The original array type was a typedef whose alignment was modified. Since the typedef has been dropped, apply the alignment directly on the array type. */ tp->alignment = explicit_alignment; tp->alignment_set_explicitly = TRUE; } /* if */ /* Either tp becomes the top of the new array type or is added on as a subarray. */ if (new_array == NULL) { new_array = tp; } else { check_assertion(prev != NULL); prev->variant.array.element_type = tp; } /* if */ prev = tp; /* Advance to the element type (which may be another array type). */ old_array = old_array->variant.array.element_type; if (is_array_type(old_array)) { /* Keep looping. */ } else { /* Done. Attach the new element type to the bottom of the chain of array types. */ tp->variant.array.element_type = element_type; break; } /* if */ } /* for */ return new_array; } /* copy_array_type_replacing_element_type */ a_type_ptr f_make_qualified_type(a_type_ptr base_type, a_type_qualifier_set qualifiers, ARG_UNUSED a_upc_block_size upc_block_size) /* Make a version of the type base_type with the additional type qualifiers indicated by the set of flags in "qualifiers". Attempt to find and reuse an existing entry if possible. The new qualifiers are added only if they are not already present. */ { a_type_ptr orig_base_type, ptr; a_boolean is_array = FALSE, expl_mem_attr_implicit = FALSE; a_type_qualifier_set base_type_qualifiers; a_type_qualifier_set qualifiers_to_add; #if NAMED_ADDRESS_SPACES_ALLOWED a_named_address_space_id nas_base, nas_to_add = 0; a_boolean nas_change_needed = FALSE; #endif /* NAMED_ADDRESS_SPACES_ALLOWED */ orig_base_type = base_type; /* According to ANSI C 3.5.3: "If the specification of an array type includes any type qualifiers, the element type is so-qualified, not the array type.", and this is interpreted recursively for arrays of arrays. The type qualifiers therefore apply to the ultimate element type. This can only happen with typedefs, as in "typedef int A[2][3]; const A a;", which makes "a" an array of array of const int. */ if (is_array_type(base_type)) { base_type = underlying_array_element_type(base_type); is_array = TRUE; } /* if */ /* Applying qualifiers to a function type is not allowed in C++. The check, if needed, should have been done by the caller. */ #if NEAR_AND_FAR_ALLOWED check_assertion(!is_function_type(base_type) || qualifiers == TQ_NONE || (near_and_far_enabled() && (qualifiers & ~(TQ_NEAR | TQ_FAR)) == TQ_NONE) || C_mode()); #else /* !NEAR_AND_FAR_ALLOWED */ check_assertion(!is_function_type(base_type) || qualifiers == TQ_NONE || C_mode()); #endif /* NEAR_AND_FAR_ALLOWED */ base_type_qualifiers = get_type_qualifiers(base_type); #if NAMED_ADDRESS_SPACES_ALLOWED if (named_address_spaces_enabled) { /* Separate the named address space qualifiers, if any. */ nas_base = named_address_space_from_qualifier_set(base_type_qualifiers); base_type_qualifiers = simple_qualifiers(base_type_qualifiers); nas_to_add = named_address_space_from_qualifier_set(qualifiers); qualifiers = simple_qualifiers(qualifiers); if (nas_base != nas_to_add) { if (nas_to_add == 0) { /* No address space is being added, so no change is required. */ } else if (nas_base == 0) { /* The base type has no named address space, so the new one is added in. */ nas_change_needed = TRUE; } else { /* There are two named address spaces, old and new. The caller should have sorted that out. */ unexpected_condition_str( "f_make_qualified_type: conflicting named address spaces"); } /* if */ } /* if */ } /* if */ #endif /* NAMED_ADDRESS_SPACES_ALLOWED */ qualifiers_to_add = qualifiers & ~base_type_qualifiers; #if NAMED_ADDRESS_SPACES_ALLOWED if (nas_change_needed) { set_named_address_space_in_qualifier_set(qualifiers_to_add, nas_to_add); } /* if */ #endif /* NAMED_ADDRESS_SPACES_ALLOWED */ #if UPC_EXTENSIONS_ALLOWED /* Always add shared if requested, so we can preserve the specified block size. */ qualifiers_to_add |= (qualifiers & TQ_UPC_SHARED); #endif /* UPC_EXTENSIONS_ALLOWED */ if (qualifiers_to_add != TQ_NONE && is_any_reference_type(base_type)) { /* cv-qualifiers are ignored when applied to a reference type. However, the "restrict" qualifier should be retained. */ qualifiers_to_add &= TQ_RESTRICT; } /* if */ if (qualifiers_to_add != TQ_NONE) { #if NEAR_AND_FAR_ALLOWED if (qualifiers_to_add & (TQ_NEAR | TQ_FAR)) { /* Don't add explicit qualifiers for memory attributes that are implied anyway. Note that even if only one qualifier is being added and it's implied, we add a typeref (containing no qualifiers) so we can put on the explicit_memory_attribute_made_implicit flag. */ a_type_qualifier_set implied_qualifier = is_far_type(base_type) ? TQ_FAR : TQ_NEAR; if (qualifiers_to_add & implied_qualifier) { /* A qualifier being added is implied. Don't add it, but record that it was explicit. */ qualifiers_to_add &= ~implied_qualifier; expl_mem_attr_implicit = TRUE; } /* if */ } /* if */ #endif /* NEAR_AND_FAR_ALLOWED */ if (qualifiers_to_add & TQ_NULLABILITY) { if (base_type_qualifiers & TQ_NULLABILITY) { /* Ignore conflicting nullability qualifiers if they weren't caught before (this is apparently how Clang operates). */ qualifiers_to_add &= ~TQ_NULLABILITY; } /* if */ } /* if */ /* Some qualifiers need to be added. */ if (base_type_qualifiers != TQ_NONE) { /* The typeref(s) containing qualifiers, if any, are removed to get down to the real base type, to which the new qualifiers are added. When a qualifier is removed, a flag must be set so that it will be added back. */ while (base_type->kind == (a_type_kind)tk_typeref) { if (typeref_is_typedef(base_type)) { /* This is a typedef -- preserve it, so that the qualifier is built on top of it. */ break; } /* if */ qualifiers_to_add |= base_type->variant.typeref.qualifiers; base_type = base_type->variant.typeref.type; } /* while */ } /* if */ /* See if the properly qualified version of base_type already exists. If so, a pointer to it is stored in the based_types for base_type. */ ptr = get_based_type(base_type, (a_based_type_kind)btk_qualified, qualifiers_to_add, PM_NONE, expl_mem_attr_implicit, /*class_type=*/(a_type_ptr)NULL, upc_block_size); if (ptr == NULL) { /* No allocated entry, need to allocate one. */ ptr = alloc_type((a_type_kind)tk_typeref); ptr->variant.typeref.type = base_type; copy_qualifiers(qualifiers_to_add, ptr->variant.typeref.qualifiers); #if NEAR_AND_FAR_ALLOWED ptr->variant.typeref.explicit_memory_attribute_made_implicit = expl_mem_attr_implicit; #endif /* NEAR_AND_FAR_ALLOWED */ #if UPC_EXTENSIONS_ALLOWED if ((qualifiers_to_add & TQ_UPC_SHARED) != TQ_NONE) { ptr->variant.typeref.extra_info->upc_block_size = upc_block_size; } /* if */ #endif /* UPC_EXTENSIONS_ALLOWED */ /* Remember the existence of this typeref type by putting a pointer to it in the based_types list. */ add_based_type_list_member(base_type, (a_based_type_kind)btk_qualified, ptr); } /* if */ if (is_array) { /* For the strange array case, the array type entries must be copied in case they are shared. */ ptr = copy_array_type_replacing_element_type(orig_base_type, ptr); /* Save a pointer to the original type on the based types list for the new type created by the copy. */ add_based_type_list_member(ptr, (a_based_type_kind)btk_unqualified_array_type, orig_base_type); } /* if */ } else { /* No qualifiers to add, so return the original type. */ ptr = orig_base_type; } /* if */ return ptr; } /* f_make_qualified_type */ a_type_ptr type_plus_qualifiers_from_second_type(a_type_ptr type, a_type_ptr model_type) /* Make a version of type that has the same qualifiers as model_type, and return a pointer to it. The original qualifiers on type, if any, are preserved, which means that the result type has all the qualifiers of both types. Note that type and model_type need not be the same (or even similar) types under the qualifiers. When UPC extensions are supported, the UPC block size must also be transferred. When named address spaces are supported, they are not transferred from model_type to type. */ { a_type_qualifier_set qualifiers = get_type_qualifiers(model_type); a_upc_block_size upc_block_size = UPC_BLOCK_SIZE_NONE; #if UPC_EXTENSIONS_ALLOWED upc_block_size = get_upc_block_size(model_type); #endif /* UPC_EXTENSIONS_ALLOWED */ #if NAMED_ADDRESS_SPACES_ALLOWED if (named_address_spaces_enabled) { /* Drop named address space qualifiers if any. */ qualifiers = simple_qualifiers(qualifiers); } /* if */ #endif /* NAMED_ADDRESS_SPACES_ALLOWED */ type = f_make_qualified_type(type, qualifiers, upc_block_size); return type; } /* type_plus_qualifiers_from_second_type */ a_type_ptr make_unqualified_type(a_type_ptr type, /* Defaulted: */ a_boolean unqualify_array_elements) /* Return a type that is the unqualified version of the type given by type. If unqualify_array_elements is TRUE, qualifiers will be removed from array elements (i.e., the typical C++ rule); otherwise, array element qualifiers will be preserved (i.e., the typical C rule). This differs from skip_typerefs in that it preserves typedefs where possible. Note that this is not the routine to use to drop qualifiers when changing to a prvalue type, except possibly for C-mode-only code; see prvalue_type instead. */ { if (is_array_type(type)) { if (unqualify_array_elements) { /* There can never be type qualifiers on top of an array type. If there are qualifiers, they are attached to the element type. Remove the qualifiers from the element type and create another array type. */ a_type_ptr element_type = underlying_array_element_type(type); if (element_type == NULL) { /* Array-of-NULL is a possible temporary state during construction of a derived type. */ } else if (is_qualified_type(element_type)) { element_type = make_unqualified_type(element_type); type = copy_array_type_replacing_element_type(type, element_type); } /* if */ } /* if */ } else { /* Non-array case. Remove the minimum number of typerefs that will produce an unqualified type. It's done in a loop in order to save typedefs if possible. */ while (is_top_level_qualified_type(type)) { type = type->variant.typeref.type; } /* while */ } /* if */ return type; } /* make_unqualified_type */ a_type_ptr remove_qualifiers(a_type_ptr type, a_type_qualifier_set qualifiers_to_remove) /* Remove the specified qualifiers from the type (if they are present) and return the resulting type. */ { a_type_ptr result = type; a_type_qualifier_set qualifiers = get_type_qualifiers(type); if (qualifiers & qualifiers_to_remove) { result = make_qualified_type(make_unqualified_type(type), qualifiers & ~qualifiers_to_remove); } /* if */ return result; } /* remove_qualifiers */ a_type_ptr remove_cvref(a_type_ptr tp) /* If tp is a reference type, replace it by the underlying type. Then, return the type obtained by removing top-level const and volatile qualifiers from tp. */ { if (is_any_reference_type(tp)) tp = type_pointed_to(tp); return remove_qualifiers(tp, TQ_CONST | TQ_VOLATILE); } /* remove_cvref */ a_type_ptr prvalue_type(a_type_ptr type) /* type is the type of a glvalue. Return the type that the associated prvalue would have. That is, drop type qualifiers as appropriate. Array-to-pointer and function-to-pointer decay are not considered. */ { a_type_ptr utp = skip_typerefs(type); if (type != utp) { /* In C++, class and array prvalues can have cv-qualified type (see [expr.type]/2 in N5008), so the cv-qualifiers are kept. In all other cases, they are removed. */ if (C_mode() || #if DO_IL_LOWERING /* Force C semantics for things created by IL lowering. */ il_lowering_underway || #endif /* DO_IL_LOWERING */ !(is_immediate_class_type(utp) || is_array_type(utp))) { type = make_unqualified_type(type); } /* if */ } /* if */ return type; } /* prvalue_type */ a_type_ptr return_type_of(a_type_ptr routine_type) /* Return the type that is the return type of the given function type. If the function returns an lvalue reference, the type is the type of the lvalue returned. Otherwise, it is the type of the rvalue returned. */ { a_type_ptr return_type; routine_type = skip_typerefs(routine_type); return_type = routine_type->variant.routine.return_type; if (!is_any_reference_type(return_type)) { /* The function returns a non-reference type, i.e., a prvalue. Drop cv-qualifiers as appropriate. */ return_type = prvalue_type(return_type); } else { /* The function returns a reference, so the return type is the type underlying the reference. */ return_type = type_pointed_to(return_type); } /* if */ return return_type; } /* return_type_of */ a_type_ptr il_return_type_of(a_type_ptr routine_type) /* Return the type that is the return type of the given function type, as it would appear as the type on a call of the function in the IL. If the function returns a reference, the reference type is returned. */ { a_type_ptr return_type; routine_type = skip_typerefs(routine_type); return_type = routine_type->variant.routine.return_type; if (is_any_reference_type(return_type)) { /* If the function returns a reference type, leave the type alone. */ } else { /* The function returns a non-reference type, so the result is a prvalue and cv-qualifiers should be dropped appropriately. */ return_type = prvalue_type(return_type); } /* if */ return return_type; } /* il_return_type_of */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_vla_dimension_ptr find_vla_dimension_in_current_function( a_type_ptr array_type) /* Find the VLA (variable-length array) dimension entry associated with the given array type, and return a pointer to it. If innermost_function_scope is not set, or if the type is not associated with the innermost function scope (possible while parsing a member of a local class), NULL is returned. For (compiler-generated) copies of array types, return the entry associated with the original type entry. */ { a_vla_dimension_ptr vlap = NULL; if (innermost_function_scope != NULL) { /*lint --e{446,850} vlap modified in loop (LINTBUG) */ for (vlap = innermost_function_scope->vla_dimensions; vlap != NULL; vlap = vlap->next) { if (vlap->type == array_type) { if (vlap->original_dimension != NULL) { /* vlap points to an entry for a compiler-generated copy of an array type. */ check_assertion(vlap->dimension_expr == NULL); vlap = vlap->original_dimension; } /* if */ check_assertion(vlap->dimension_expr != NULL); break; } /* if */ } /* for */ } /* if */ return vlap; } /* find_vla_dimension_in_current_function */ a_vla_dimension_ptr find_vla_dimension(a_type_ptr array_type) /* Find the VLA (variable-length array) dimension entry associated with the given array type, and return a pointer to it. innermost_function_scope must be set to the scope for the current function. For (compiler-generated) copies of array types, return the entry associated with the original type entry. */ { a_vla_dimension_ptr vlap = find_vla_dimension_in_current_function(array_type); check_assertion_str(vlap != NULL, "find_vla_dimension: not found"); return vlap; } /* find_vla_dimension */ #if !STANDALONE_UTILITY_PROGRAM void make_local_expr_node_ref(an_expr_node_ptr expr, a_local_expr_node_ref_kind kind, char *referrer, a_scope_ptr func_scope) /* expr is a node in a function's memory region -- associated with func_scope -- and referrer is an entry in the file scope memory region. Create an entry in that function's memory region to represent an implicit reference from referrer to expr. kind indicates the nature of the referrer. (This is needed because file scope memory entries cannot directly point to entries in function scope memory regions.) func_scope describes the function where the expression is stored. Note that referrer must be an IL entity that has an initial a_source_correspondence field. The expression can then be recovered using find_local_expr_node. */ { a_memory_region_number memory_region, region_to_switch_back_to; a_local_expr_node_ref_ptr new_ref; a_routine_ptr rp; check_assertion(!in_file_scope(expr) && in_file_scope(referrer) && func_scope != NULL); check_assertion(scope_is(func_scope, sck_function)); rp = func_scope->variant.routine.ptr; ((a_source_correspondence*)referrer)->enclosing_routine = rp; memory_region = mem_region_for_routine(rp); if (memory_region != curr_il_region_number) { region_to_switch_back_to = curr_il_region_number; switch_il_region(memory_region); } else { region_to_switch_back_to = NULL_region_number; } /* if */ new_ref = alloc_local_expr_node_ref(); switch_back_to_original_region(region_to_switch_back_to); new_ref->next = func_scope->expr_node_refs; new_ref->expr = expr; new_ref->kind = kind; new_ref->referrer.ptr = referrer; check_assertion(!in_file_scope(new_ref)); switch (kind) { #if PROTOTYPE_INSTANTIATIONS_IN_IL case lerk_generic_sizeof: new_ref->referrer.kind = iek_constant; check_assertion(!((a_constant_ptr)referrer) ->variant.template_param.local_expr_ref); ((a_constant_ptr)referrer)->variant.template_param.local_expr_ref = TRUE; break; #endif /* PROTOTYPE_INSTANTIATIONS_IN_IL */ case lerk_array_bound: new_ref->referrer.kind = iek_type; check_assertion(!((a_type_ptr)referrer)->variant.array. constant_bound_expr_in_local_expr_node_ref); ((a_type_ptr)referrer)->variant.array. constant_bound_expr_in_local_expr_node_ref = TRUE; break; case lerk_dep_array_bound: new_ref->referrer.kind = iek_type; check_assertion(!((a_type_ptr)referrer)->variant.array. dep_constant_bound_expr_in_local_expr_node_ref); ((a_type_ptr)referrer)->variant.array. dep_constant_bound_expr_in_local_expr_node_ref = TRUE; break; case lerk_decltype: new_ref->referrer.kind = iek_type; break; #if PROTOTYPE_INSTANTIATIONS_IN_IL case lerk_tpl_param_expr: new_ref->referrer.kind = iek_constant; check_assertion(!((a_constant_ptr)referrer) ->variant.template_param.local_expr_ref); ((a_constant_ptr)referrer)->variant.template_param.local_expr_ref = TRUE; break; #endif /* PROTOTYPE_INSTANTIATIONS_IN_IL */ case lerk_bit_field_width: new_ref->referrer.kind = iek_field; check_assertion(!((a_field_ptr)referrer)-> bit_size_constant_expr_in_local_expr_node_ref); ((a_field_ptr)referrer)->bit_size_constant_expr_in_local_expr_node_ref = TRUE; break; case lerk_constant_expr: new_ref->referrer.kind = iek_constant; check_assertion(!((a_constant_ptr)referrer)->local_expr_ref); ((a_constant_ptr)referrer)->local_expr_ref = TRUE; break; case lerk_scoped_expr: /* Note that an_attribute_arg::local_expr_ref is set in the caller. */ new_ref->referrer.kind = iek_scoped_expression; break; case lerk_none: break; default_is_unexpected(); } /* switch */ func_scope->expr_node_refs = new_ref; } /* make_local_expr_node_ref */ void add_local_expr_node_referrer(char *new_referrer, char *old_referrer, a_local_expr_node_ref_kind kind) /* The IL entry given by new_referrer is a duplicate of the old_referrer entry and the latter has an associated a_local_expr_node_ref entry of the given kind. Duplicate that entry but associate it with the given referrer. */ { a_source_correspondence *scp = (a_source_correspondence*)new_referrer; a_routine *rp = scp->enclosing_routine; if (rp->function_def_number != NULL_function_def_number) { a_scope_ptr scope = scope_for_routine(rp); a_local_expr_node_ref_ptr ref = scope->expr_node_refs, new_ref; for (; ref != NULL; ref = ref->next) { if (ref->kind == kind && ref->referrer.ptr == old_referrer) { a_memory_region_number memory_region, region_to_switch_back_to; memory_region = mem_region_for_routine(rp); if (memory_region != curr_il_region_number) { region_to_switch_back_to = curr_il_region_number; switch_il_region(memory_region); } else { region_to_switch_back_to = NULL_region_number; } /* if */ new_ref = alloc_local_expr_node_ref(); switch_back_to_original_region(region_to_switch_back_to); new_ref->next = ref->next; ref->next = new_ref; new_ref->kind = kind; new_ref->expr = ref->expr; new_ref->referrer.ptr = new_referrer; new_ref->referrer.kind = ref->referrer.kind; break; } /* if */ } /* for */ } /* if */ } /* add_local_expr_node_referrer */ #endif /* !STANDALONE_UTILITY_PROGRAM */ an_expr_node_ptr find_local_expr_node_in_scope( char *referrer, a_local_expr_node_ref_kind kind, a_scope_ptr target_scope) /* referrer is an entry in the file scope memory region that implicitly refers to an expression in target_scope's memory region. If target_scope is non-NULL and if it is the function containing that expression, return a pointer to that expression. Otherwise, return NULL. (The expression pointer is determined by searching a list of a_local_expr_node_ref entries. kind represents the kind of entry that is expected to hold a pointer to the expression being searched for.) See find_local_expr_node for typical use. */ { an_expr_node_ptr result = NULL; if (target_scope != NULL) { a_local_expr_node_ref_ptr ref = target_scope->expr_node_refs; for (; ref != NULL; ref = ref->next) { if (ref->referrer.ptr == referrer && ref->kind == kind) { result = ref->expr; break; } /* if */ } /* for */ } /* if */ return result; } /* find_local_expr_node_in_scope */ an_expr_node_ptr find_local_expr_node(char *referrer, a_local_expr_node_ref_kind kind) /* referrer is an entry in the file scope memory region that implicitly refers to an expression in a function scope memory region. If the innermost function scope is non-NULL and if it is the function containing that expression, return a pointer to that expression. Otherwise, return NULL. (The expression pointer is determined by searching a list of a_local_expr_node_ref entries. kind represents the kind of entry that is expected to hold a pointer to the expression being searched for.) */ { a_source_correspondence *scp = (a_source_correspondence*)referrer; an_expr_node_ptr result; if (scp->enclosing_routine->function_def_number != NULL_function_def_number) { a_scope_ptr scope = scope_for_routine(scp->enclosing_routine); result = find_local_expr_node_in_scope(referrer, kind, scope); } else { result = NULL; } /* if */ return result; } /* find_local_expr_node */ an_expr_node_ptr expr_node_from_tpck_expression(const a_constant *cp) /* Return the expression associated with cp, which must be a ck_template_param/tpck_expression constant. */ { an_expr_node_ptr expr; check_assertion(constant_is(cp, ck_template_param) && tpck_is(cp, tpck_expression)); #if PROTOTYPE_INSTANTIATIONS_IN_IL if (cp->variant.template_param.local_expr_ref) { /* This constant is in file scope memory but the associated expression is in function scope memory. Use the local expression node mechanism to find it. */ expr = find_local_expr_node( (char *)cp, (a_local_expr_node_ref_kind)lerk_tpl_param_expr); } else #endif /* PROTOTYPE_INSTANTIATIONS_IN_IL */ /* Do not insert code here. */ { expr = cp->variant.template_param.variant.expr; } /* if */ return expr; } /* expr_node_from_tpck_expression */ an_expr_node_ptr expr_node_from_constant(a_constant_ptr cp) /* Return the expression node, if any, associated with cp: the backing expression, if present, otherwise the expression for the specific variant of the constant. Find the node via the local expression node mechanism if necessary. */ { an_expr_node_ptr expr = cp->expr; if (expr == NULL) { if (constant_is(cp, ck_template_param)) { if (tpck_is(cp, tpck_expression)) { expr = cp->variant.template_param.variant.expr; } else if (tpck_is(cp, tpck_sizeof) || tpck_is(cp, tpck_datasizeof) || tpck_is(cp, tpck_alignof) || tpck_is(cp, tpck_uuidof) || tpck_is(cp, tpck_typeid) || tpck_is(cp, tpck_noexcept)) { expr = cp->variant.template_param.variant.templ_sizeof.expr; } /* if */ } /* if */ } /* if */ if (expr == NULL && (cp->local_expr_ref || (constant_is(cp, ck_template_param) && cp->variant.template_param.local_expr_ref))) { /* The expression involves entities local to a function and thus must be accessed via the local expression node mechanism. */ a_scope_ptr sp; if (cp->source_corresp.enclosing_routine != NULL && cp->source_corresp.enclosing_routine->function_def_number != NULL_function_def_number) { sp = scope_for_routine_or_null(cp->source_corresp.enclosing_routine); } else { sp = innermost_function_scope; } /* if */ if (sp != NULL) { if (cp->local_expr_ref) { expr = find_local_expr_node_in_scope( (char *)cp, (a_local_expr_node_ref_kind)lerk_constant_expr, sp); } else { expr = find_local_expr_node_in_scope( (char *)cp, (a_local_expr_node_ref_kind)lerk_tpl_param_expr, sp); } /* if */ } /* if */ } /* if */ return expr; } /* expr_node_from_constant */ an_expr_node_ptr expr_node_from_attribute_arg(an_attribute_arg_ptr aap) /* Return the expression associated with the aak_expression attribute argument. This routine is used to hide whether or not the expression is in a different memory region. */ { an_expr_node_ptr result = NULL; check_assertion(aap->kind == aak_expression); if (aap->local_expr_ref) { a_scoped_expression *sexpr = aap->variant.sexpr; check_assertion(result == NULL && sexpr->source_corresp.enclosing_routine != NULL); a_scope_ptr scope = scope_for_routine( sexpr->source_corresp.enclosing_routine); result = find_local_expr_node_in_scope((char*)sexpr, lerk_scoped_expr, scope); } else { result = aap->variant.expr; } /* if */ return result; } /* expr_node_from_attribute_arg */ #if !STANDALONE_UTILITY_PROGRAM void make_local_scope_ref(a_scope_ptr scope, char *referrer, an_il_entry_kind referrer_kind, a_scope_ptr func_scope) /* scope is a scope in a function's memory region -- associated with func_scope -- and referrer is an entry (of the given kind) in the file scope memory region. Create an entry in that function's memory region to represent an implicit reference from referrer to scope. (This is needed because file scope memory entries cannot directly point to entries in function scope memory regions.) func_scope describes the function where *scope is stored. The scope can then be recovered using find_local_scope. */ { a_memory_region_number memory_region, region_to_switch_back_to; a_local_scope_ref_ptr new_ref; check_assertion(!in_file_scope(scope) && in_file_scope(referrer)); check_assertion(func_scope->kind == (a_scope_kind)sck_function); memory_region = mem_region_for_routine(func_scope->variant.routine.ptr); if (memory_region != curr_il_region_number) { region_to_switch_back_to = curr_il_region_number; switch_il_region(memory_region); } else { region_to_switch_back_to = NULL_region_number; } /* if */ new_ref = alloc_local_scope_ref(); switch_back_to_original_region(region_to_switch_back_to); new_ref->next = func_scope->scope_refs; new_ref->scope = scope; new_ref->referrer.ptr = referrer; new_ref->referrer.kind = referrer_kind; check_assertion(!in_file_scope(new_ref)); func_scope->scope_refs = new_ref; } /* make_local_scope_ref */ #endif /* !STANDALONE_UTILITY_PROGRAM */ static a_scope_ptr find_local_scope_in_function_scope(char *referrer, a_scope_ptr fn_scope) /* referrer is an entry in the file scope memory region that implicitly refers to a scope L in a function scope memory region determined by fn_scope. If fn_scope is non-NULL and if it is the function containing scope L, return a pointer to L. Otherwise, return NULL. (The result is determined by searching a list of a_local_scope_ref entries.) */ { a_scope_ptr result = NULL; if (fn_scope != NULL) { a_local_scope_ref_ptr ref = fn_scope->scope_refs; for (; ref != NULL; ref = ref->next) { if (ref->referrer.ptr == referrer) { result = ref->scope; break; } /* if */ } /* for */ } /* if */ return result; } /* find_local_scope_in_function_scope */ a_scope_ptr find_local_scope(char *referrer) /* Call find_local_scope_in_function_scope with innermost_function_scope. */ { return find_local_scope_in_function_scope(referrer, innermost_function_scope); } /* find_local_scope */ a_scope_ptr f_get_parent_scope_of(a_source_correspondence_ptr scp) /* Determine and return the parent scope of the entity associated with scp. This may involve searching a list of a_local_scope_ref entries. This routine is best called through the macro get_parent_scope_of. Returns NULL if the scope cannot be found (e.g., because it's a function scope that's no longer in memory). */ { a_scope_ptr result = scp->parent_scope; if (scp->parent_via_local_scope_ref) { check_assertion(result == NULL && scp->enclosing_routine != NULL); if (scp->enclosing_routine->function_def_number != NULL_function_def_number) { a_scope_ptr enclosing_fn_scope = scope_for_routine(scp->enclosing_routine); result = find_local_scope_in_function_scope((char*)scp, enclosing_fn_scope); } /* if */ } /* if */ return result; } /* f_get_parent_scope_of */ a_scope_ptr get_assoc_scope_of_il_entry(char *entity, an_il_entry_kind kind) /* Given an IL entity, return the associated scope (e.g., for a namespace, return the namespace's scope). If there is no associated scope, return NULL. For class types, the scope here may be NULL if the class type needs to be completed but hasn't. Since that has an impact on the semantics of the code, the caller must ensure the class type is complete before calling this routine. */ { a_scope_ptr result = NULL; switch (kind) { case iek_routine: { a_routine_ptr ptr = (a_routine_ptr)entity; result = scope_for_routine_or_null(ptr); } break; case iek_for_loop: { a_for_loop_ptr ptr = (a_for_loop_ptr)entity; result = ptr->for_init_scope; } break; case iek_range_based_for_loop: { a_range_based_for_loop_ptr ptr = (a_range_based_for_loop_ptr)entity; result = ptr->range_based_for_scope; } break; #if MICROSOFT_EXTENSIONS_ALLOWED case iek_for_each_loop: { a_for_each_loop_ptr ptr = (a_for_each_loop_ptr)entity; result = ptr->for_each_scope; } break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case iek_block: { a_block_ptr ptr = (a_block_ptr)entity; result = ptr->assoc_scope; } break; case iek_statement: { a_statement_ptr ptr = (a_statement_ptr)entity; switch (ptr->kind) { case stmk_block: entity = (char*)ptr->variant.block.extra_info; kind = iek_block; break; case stmk_for: #if UPC_EXTENSIONS_ALLOWED case stmk_upc_forall: #endif /* UPC_EXTENSIONS_ALLOWED */ entity = (char*)ptr->variant.for_loop.extra_info; kind = iek_for_loop; break; case stmk_range_based_for: entity = (char*)ptr->variant.range_based_for_loop.extra_info; kind = iek_range_based_for_loop; break; #if MICROSOFT_EXTENSIONS_ALLOWED case stmk_for_each: entity = (char*)ptr->variant.for_each_loop.extra_info; kind = iek_for_each_loop; break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case stmk_try_block: entity = (char*)ptr->variant.try_block; kind = iek_try_supplement; break; case stmk_while: case stmk_end_test_while: entity = (char*)ptr->variant.loop_statement; kind = iek_statement; break; default: entity = NULL; kind = iek_none; } /* switch */ if (entity != NULL) { result = get_assoc_scope_of_il_entry(entity, kind); } /* if */ } break; case iek_try_supplement: { a_try_supplement_ptr ptr = (a_try_supplement_ptr)entity; result = get_assoc_scope_of_il_entry((char*)ptr->statement, iek_statement); } break; case iek_namespace: { a_namespace_ptr ptr = (a_namespace_ptr)entity; result = skip_namespace_aliases(ptr)->variant.assoc_scope; } break; case iek_overriding_virtual_function: { an_overriding_virtual_function_ptr ptr = (an_overriding_virtual_function_ptr)entity; result = get_assoc_scope_of_il_entry((char*)ptr->overriding_function, iek_routine); } break; case iek_template: { a_template_ptr ptr = (a_template_ptr)entity; result = get_assoc_scope_of_il_entry((char*)ptr->template_decl, iek_template_decl); } break; case iek_template_decl: { a_template_decl_ptr ptr = (a_template_decl_ptr)entity; result = ptr->scope; } break; case iek_local_scope_ref: { a_local_scope_ref_ptr ptr = (a_local_scope_ref_ptr)entity; result = ptr->scope; } break; case iek_lambda: { a_lambda_ptr ptr = (a_lambda_ptr)entity; result = get_assoc_scope_of_il_entry((char*)ptr->lambda_routine, iek_routine); } break; case iek_type: { a_type_ptr ptr = (a_type_ptr)entity; ptr = skip_typerefs(ptr); if (is_class_struct_union_type(ptr)) { a_class_type_supplement_ptr ctsp = class_type_supp(ptr); if (ctsp != NULL) { result = ctsp->assoc_scope; } /* if */ } else if (is_enum_type(ptr)) { if (is_scoped_enum_type(ptr)) { result = ptr->variant.integer.enum_info.assoc_scope; } else { result = ptr->source_corresp.parent_scope; } /* if */ } /* if */ } break; default: break; } /* switch */ return result; } /* get_assoc_scope_of_il_entry */ a_routine_ptr lambda_body_for_closure(a_type_ptr type) /* Return a pointer to the lambda body routine for the closure class specified by type, or NULL if the lambda body routine does not exist yet. */ { a_routine_ptr rp = NULL; a_scope_ptr scope = class_type_supp(type)->assoc_scope; if (!scope_is_null_or_placeholder(scope)) { if (!class_type_supp(type)->is_generic_lambda_closure_class) { /* A normal (i.e., non-generic) lambda. Look among the routine entries in the class scope. */ for (rp = scope->routines; rp != NULL; rp = rp->next) { if (rp->is_lambda_body) break; } /* for */ } else { /* A generic lambda: Look among the template entries in the class scope. Use the symbols list since scope->templates doesn't always include member templates if prototype_instantiations_in_il is FALSE. */ a_symbol_ptr sym; if (scope->depth_in_scope_stack != NO_SCOPE_DEPTH) { /* If the class is still being defined, the symbols list may not have been updated yet, but it is available in the pointers block associated with the class. */ sym = class_symbol_supp(symbol_for(type))->pointers_block.symbols; } else { sym = class_symbol_supp(symbol_for(type))->symbols; } /* if */ for (; sym != NULL; sym = sym->next_in_scope) { if (symbol_is(sym, sk_function_template)) { a_routine_ptr proto_rp = sym->variant.template_info ->variant.function.routine; if (proto_rp->is_lambda_body) { rp = proto_rp; break; } /* if */ } /* if */ } /* for */ } /* if */ } /* if */ return rp; } /* lambda_body_for_closure */ #if PROTOTYPE_INSTANTIATIONS_IN_IL an_expr_node_ptr generic_sizeof_arg_expr(a_constant_ptr cp) /* The given constant is a ck_template_param of kind tpck_sizeof representing a sizeof, __datasizeof, alignof, uuidof, typeid, or noexcept construct. If the construct had an expression argument return that expression, otherwise, return NULL. The expression may be referred to indirectly through an entry of type a_local_expr_node_ref. */ { an_expr_node_ptr result; check_assertion(constant_is(cp, ck_template_param) && (tpck_is(cp, tpck_sizeof) || tpck_is(cp, tpck_datasizeof) || tpck_is(cp, tpck_alignof) || tpck_is(cp, tpck_uuidof) || tpck_is(cp, tpck_typeid) || tpck_is(cp, tpck_noexcept))); result = cp->variant.template_param.variant.templ_sizeof.expr; if (result == NULL && cp->variant.template_param.local_expr_ref) { /* The argument of the sizeof/alignof/uuidof/typeid construct is an expression, whose representation is stored in a function scope memory region. Since we are currently inside a function, look if the expression is part of this function. This is not always the case. For example: template struct S {}; template void f1(T x1) { int const n1 = sizeof(x1); S s1; } template void f2(T x2) { int const n2 = sizeof(x2); S s2; } When looking whether the nonreal instance S already exists, the instance S may be considered, but the representation of n1 will no longer be available. */ result = find_local_expr_node((char*)cp, lerk_generic_sizeof); } /* if */ return result; } /* generic_sizeof_arg_expr */ #endif /* PROTOTYPE_INSTANTIATIONS_IN_IL */ #if !STANDALONE_UTILITY_PROGRAM a_type_ptr make_field_selection_type(a_field_ptr field, a_type_qualifier_set qualifiers) /* As part of determining the result type of a field selection, return a type based on the type of the specified field, with the indicated qualifiers added. However, if the field was declared mutable, "const" in the qualifier set is ignored. */ { a_type_ptr type = field->type; #if GNU_EXTENSIONS_ALLOWED if (gcc_mode && field->is_bit_field && targ_char_bit == 8) { /* GCC treats bit fields that are the exact width of an integer type as having a corresponding integer type. For example, with: struct S { unsigned u:8; } s; the expression s.u has type unsigned char. */ a_type_mode_kind mode; switch (field->bit_size) { case 8: mode = tmk_QI; break; case 16: mode = tmk_HI; break; case 32: mode = tmk_SI; break; case 64: mode = tmk_DI; break; case 128: mode = tmk_TI; break; default: mode = tmk_none; break; } /* switch */ if (mode != tmk_none) { type = get_type_with_mode(type, mode, (a_source_position*)NULL); } /* if */ } /* if */ #endif /* GNU_EXTENSIONS_ALLOWED */ /* The selected field has all the type qualifiers of both the field and the selecting pointer -- except that const is removed if the field was declared to be mutable. */ if (field->is_mutable) qualifiers &= ~TQ_CONST; #if UPC_EXTENSIONS_ALLOWED /* Fields cannot be shared without the enclosing structure being shared. If the field is an array, the type must be constructed so that all elements are on the same thread. This is, by definition, infinite block size, so specify that here. */ type = f_make_qualified_type(type, qualifiers, ((qualifiers & TQ_UPC_SHARED) != 0) ? UPC_BLOCK_SIZE_INDEFINITE : UPC_BLOCK_SIZE_NONE); #else /* UPC_EXTENSIONS_ALLOWED */ type = make_qualified_type(type, qualifiers); #endif /* UPC_EXTENSIONS_ALLOWED */ return type; } /* make_field_selection_type */ a_type_ptr make_pm_selection_type(a_type_ptr operand_1_type, a_type_ptr operand_2_type) /* Determine the result type of a pointer-to-member selection operator, i.e., ".*" or ".->". operand_1_type is the type of the first operand, standardized to class form (i.e., for the "->*" case one level of pointer has been stripped off). operand_2_type is the pointer-to-member type of the second operand. */ { a_type_ptr result_type; if (is_error_type(operand_2_type)) { result_type = error_type(); } else { #if DO_IL_LOWERING result_type = pm_member_type_possibly_lowered(operand_2_type); #else /* !DO_IL_LOWERING */ result_type = pm_member_type(operand_2_type); #endif /* DO_IL_LOWERING */ if (!(microsoft_mode && microsoft_version < 1200) && !is_function_type(result_type)) { /* Add cv-qualifiers from the first operand to the result type. (This was not in the ARM, but it's in the standard, and cfront does it.) Note: this isn't done for the pointer-to-member-function case, since applying type qualifiers to a function type is not allowed. */ result_type = type_plus_qualifiers_from_second_type(result_type, operand_1_type); } /* if */ } /* if */ return result_type; } /* make_pm_selection_type */ void skip_common_type_qualifiers(a_type_ptr *type1, a_type_ptr *type2) /* If both *type1 and *type2 have any of the same type qualifiers, strip off the common qualifiers but leave any other type qualifiers in place. Both types may be modified, or neither, or only one of the two. If both original types have qualifiers, skip_typerefs is called to get down to the base types, and then new types may be built up from them; this may result in discarding typedefs. */ { a_type_qualifier_set type1_qualifiers; a_type_qualifier_set type2_qualifiers; a_type_qualifier_set qualifiers; a_type_ptr tp1 = *type1, tp2 = *type2; type1_qualifiers = get_type_qualifiers(tp1); type2_qualifiers = get_type_qualifiers(tp2); if (type1_qualifiers != TQ_NONE && type2_qualifiers != TQ_NONE) { /* Strip off the qualifiers. */ tp1 = make_unqualified_type(tp1); tp2 = make_unqualified_type(tp2); if (type1_qualifiers ^ type2_qualifiers) { /* They are differently qualified. The qualifiers have been stripped off; add them back on as appropriate. */ qualifiers = type1_qualifiers & ~type2_qualifiers; if (qualifiers != TQ_NONE) { tp1 = make_qualified_type(tp1, qualifiers); } /* if */ qualifiers = type2_qualifiers & ~type1_qualifiers; if (qualifiers != TQ_NONE) { tp2 = make_qualified_type(tp2, qualifiers); } /* if */ } else { /* Either both are const, both are volatile, or both are const volatile. Return both types with all qualifiers stripped off. */ } /* if */ *type1 = tp1; *type2 = tp2; } /* if */ } /* skip_common_type_qualifiers */ static a_param_type_ptr copy_param_type_list( a_param_type_ptr ptp, a_boolean copy_default_args, uint32_t max_params) /* Copy the param-type list pointed to by ptp and return a pointer to the new list. If copy_default_args is TRUE, copy any default argument expressions into the new param types. If it is FALSE, the default_arg_expr field in the new param types will be NULL. If max_params is not zero, copy no more than the given number of parameter entries. */ { a_param_type_ptr new_list = NULL, new_ptp, prev_new_ptp = NULL; uint32_t n_copied = 0; for (; ptp != NULL; ptp = ptp->next) { /* Pass a NULL source position to make_param_type to avoid inappropriate diagnostics on a type that doesn't correspond directly to a source construct. */ new_ptp = make_param_type(ptp->type, &null_source_position); /* Do a struct copy from the old param type to the new. */ *new_ptp = *ptp; if (ptp->has_default_arg) { /* This parameter has a default argument. Copy it or not, as directed. */ if (copy_default_args) { if (ptp->has_unevaluated_template_default) { /* This default argument hasn't been evaluated yet. The fields needed to evaluate it later (has_unevaluated_template_default and orig_param_type_for_unevaluated_default_arg_expr) were copied above. In some cases (e.g., prototype instantiations), there may still be a default arg expression pointer - NULL it out since we aren't copying it here. */ new_ptp->default_arg_expr = NULL; } else if (ptp->default_arg_expr != NULL) { /* Expressions may not be shared -- that is, they may not be pointed to from more than one place. Therefore a copy must be made of the expression node for the default arg (if one exists). */ new_ptp->default_arg_expr = duplicate_default_arg_expr(ptp->default_arg_expr); } else { check_assertion(is_at_least_one_error()); } /* if */ } else { /* The default argument should not be copied. */ new_ptp->has_default_arg = FALSE; new_ptp->default_arg_expr = NULL; new_ptp->has_unevaluated_template_default = FALSE; new_ptp->orig_param_type_for_unevaluated_default_arg_expr = NULL; } /* if */ } else { /* This parameter has no default argument. */ check_assertion(ptp->default_arg_expr == NULL && !ptp->has_unevaluated_template_default && ptp->orig_param_type_for_unevaluated_default_arg_expr == NULL); } /* if */ new_ptp->attributes = copy_of_attributes_list(ptp->attributes); #if MICROSOFT_EXTENSIONS_ALLOWED /* Duplicate the Microsoft attributes list (if any). */ if (ptp->ms_attributes != NULL) { new_ptp->ms_attributes = duplicate_ms_attributes(ptp->ms_attributes, (char*)new_ptp); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Note: the name associated with the original param type entry is preserved in the copy. */ if (new_list == NULL) { new_list = new_ptp; } else { check_assertion(prev_new_ptp != NULL); prev_new_ptp->next = new_ptp; } /* if */ prev_new_ptp = new_ptp; ++n_copied; if (n_copied == max_params) { new_ptp->next = NULL; break; } /* if */ } /* for */ return new_list; } /* copy_param_type_list */ void copy_type_full(a_type_ptr from, a_type_ptr to, a_boolean copy_default_args) /* Copy the type entry "from" to "to". When the type being copied is a routine type, its routine type supplement and parameter type list are copied too (the destination type must be a routine type also, so that it already has an allocated routine type supplement); if copy_default_args is TRUE, any default argument expressions are copied into the new parameter types. If it is FALSE, the default_arg_expr field in the new parameter types will be NULL. */ { a_type_kind from_kind; a_routine_type_supplement_ptr rtsp = NULL; a_typeref_type_supplement_ptr ttsp = NULL; a_type_ptr next_ptr, tp; a_dependent_type_fixup_kind dtf_kind; from_kind = from->kind; /* If one type is a routine, both must be. */ check_assertion((from_kind == (a_type_kind)tk_routine) == (to->kind == (a_type_kind)tk_routine)); check_assertion((from_kind == (a_type_kind)tk_typeref) == (to->kind == (a_type_kind)tk_typeref)); if (from_kind == (a_type_kind)tk_routine) { /* For a routine type, preserve the type supplement pointer for the copy below. */ rtsp = to->variant.routine.extra_info; } else if (from_kind == (a_type_kind)tk_typeref) { /* For a typeref type, preserve the type supplement pointer for the copy below. */ ttsp = to->variant.typeref.extra_info; } /* if */ /* Preserve the "next" pointer in the "to" entry. */ next_ptr = to->next; /* Copy the type entry. */ *to = *from; clear_type_cached_flags(to); #if MAINTAIN_NEEDED_FLAGS reset_needed_flag(&to->source_corresp); #endif /* MAINTAIN_NEEDED_FLAGS */ to->next = next_ptr; to->based_types = NULL; #if EXTRA_SOURCE_POSITIONS_IN_IL to->source_corresp.decl_pos_info = NULL; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ #if LOWER_VARIABLE_LENGTH_ARRAYS to->visited_for_vla_lowering = FALSE; #endif /* LOWER_VARIABLE_LENGTH_ARRAYS */ if (from_kind == tk_array || from_kind == tk_routine) { if (from_kind == tk_routine) { /* For a routine type, the type supplement must also be copied. */ *rtsp = *from->variant.routine.extra_info; to->variant.routine.extra_info = rtsp; /* Copy the parameter type list. */ rtsp->param_type_list = copy_param_type_list(function_type_params(from), copy_default_args, /*max_params=*/(uint32_t)0); tp = skip_typerefs(to->variant.routine.return_type); dtf_kind = (a_dependent_type_fixup_kind)dtfk_routine_calling_method; } else { /* An array type. */ tp = f_skip_typerefs(underlying_array_element_type(to)); dtf_kind = dtfk_array_type_size; if (from->variant.array.has_assoc_vla_dimension) { /* A variable-length array (VLA): Create a new a_vla_dimension entry for the new copy of the type. VLA types can only be copied while the function scope of the original is still active. However, in some cases, it is possible that a function prototype scope is the current scope for new declarations. To avoid confusing the make_vla_dimension routine, we temporarily reset decl_scope_level back to depth_innermost_function_scope. */ a_scope_depth saved_decl_scope_level = decl_scope_level; a_vla_dimension_ptr vdp = find_vla_dimension(from), new_vdp; decl_scope_level = depth_innermost_function_scope; new_vdp = make_vla_dimension(to, (an_expr_node_ptr)NULL, (a_boolean)vdp->in_prototype_scope, &vdp->position); decl_scope_level = saved_decl_scope_level; new_vdp->original_dimension = vdp; } /* if */ if (to->variant.array.constant_bound_expr_in_local_expr_node_ref) { add_local_expr_node_referrer((char*)to, (char*)from, lerk_array_bound); } /* if */ if (to->variant.array.dep_constant_bound_expr_in_local_expr_node_ref) { add_local_expr_node_referrer((char*)to, (char*)from, lerk_dep_array_bound); } /* if */ } /* if */ if (is_incomplete_type(tp) && is_immediate_class_type(tp)) { /* An array type is placed on a fixup list if the underlying element type is an incomplete class type (allowed by extension), and a routine type is placed on the list if its return type is an incomplete class type. */ /* Pass the NULL source position since no errors should be issued about this type. */ add_to_dependent_type_fixup_list(tp, dtf_kind, (char *)to, iek_type, &null_source_position); } /* if */ } else if (from_kind == (a_type_kind)tk_typeref) { /* For a typeref, copy the supplement. */ *ttsp = *from->variant.typeref.extra_info; to->variant.typeref.extra_info = ttsp; } /* if */ } /* copy_type_full */ void copy_type(a_type_ptr from, a_type_ptr to) /* Copy the type entry "from" to "to". */ { copy_type_full(from, to, /*copy_default_args=*/TRUE); } /* copy_type */ a_type_ptr type_without_deduced_auto_placeholder(a_type_ptr type) /* If the given type includes a typeref representing a deduced "auto" or "decltype(auto)" type, return an equivalent type entry without that placeholder. Otherwise, return the given type. */ { a_type_ptr result; if (type_is(type, tk_typeref) && (is_typeref_kind(type, trk_is_deduced_auto) || is_typeref_kind(type, trk_is_deduced_decltype_auto))) { result = type->variant.typeref.type; } else { /* Look for an embedded "auto" placeholder ("decltype(auto)" can only appear at the top level and that case was handled above). */ a_boolean has_auto = FALSE, done = FALSE; a_type_ptr tp = type; while (!done) { switch (tp->kind) { case tk_pointer: tp = tp->variant.pointer.type; break; case tk_array: tp = tp->variant.array.element_type; break; case tk_typeref: if (is_typeref_kind(tp, trk_is_deduced_auto)) { has_auto = TRUE; done = TRUE; break; } /* if */ tp = tp->variant.typeref.type; break; case tk_ptr_to_member: tp = tp->variant.ptr_to_member.type; break; default: done = TRUE; } /* switch */ } /* while */ if (!has_auto) { result = type; } else { /* Copy the type tree above the placeholder entry first. */ a_type_ptr *p_new_tp = &result; tp = type; done = FALSE; while (!(type_is(tp, tk_typeref) && is_typeref_kind(tp, trk_is_deduced_auto))) { *p_new_tp = alloc_type(tp->kind); copy_type_full(tp, *p_new_tp, /*copy_default_args=*/FALSE); switch (tp->kind) { case tk_pointer: tp = tp->variant.pointer.type; p_new_tp = &(*p_new_tp)->variant.pointer.type; break; case tk_array: tp = tp->variant.array.element_type; p_new_tp = &(*p_new_tp)->variant.array.element_type; break; case tk_typeref: tp = tp->variant.typeref.type; p_new_tp = &(*p_new_tp)->variant.typeref.type; break; case tk_ptr_to_member: tp = tp->variant.ptr_to_member.type; p_new_tp = &(*p_new_tp)->variant.ptr_to_member.type; break; default: unexpected_condition(); } /* switch */ } /* while */ /* Make the new type point directly to the type underlying the placeholder. */ *p_new_tp = tp->variant.typeref.type; } /* if */ } /* if */ return result; } /* type_without_deduced_auto_placeholder */ void copy_routine_type_default_args(a_type_ptr from_type, a_type_ptr to_type) /* from_type and to_type are routine types with identical param-type lists -- except that the param-type entries of from_type may have default arguments that are missing from to_type. Copy the default argument expressions from from_type to to_type. This should be called only in C++ mode. */ { a_param_type_ptr from_ptp, to_ptp; db_enter(5, "copy_routine_type_default_args"); check_assertion(!C_mode()); from_ptp = skip_typerefs(from_type)-> variant.routine.extra_info->param_type_list; to_ptp = skip_typerefs(to_type)-> variant.routine.extra_info->param_type_list; for (;; from_ptp = from_ptp->next, to_ptp = to_ptp->next) { check_assertion((from_ptp == NULL) == (to_ptp == NULL)); if (from_ptp == NULL) break; if (from_ptp->has_default_arg) { to_ptp->has_default_arg = TRUE; to_ptp->default_arg_appeared_in_class_definition = from_ptp->default_arg_appeared_in_class_definition; if (from_ptp->has_unevaluated_template_default) { to_ptp->has_unevaluated_template_default = TRUE; to_ptp->orig_param_type_for_unevaluated_default_arg_expr = from_ptp; } /* if */ if (from_ptp->default_arg_expr != NULL) { if (to_ptp->default_arg_expr == NULL) { to_ptp->default_arg_expr = duplicate_default_arg_expr(from_ptp->default_arg_expr); } else { check_assertion(is_at_least_one_error() || compare_expressions(from_ptp->default_arg_expr, to_ptp->default_arg_expr, CC_NO_OPTIONS)); } /* if */ } /* if */ } /* if */ } /* for */ db_exit(); } /* copy_routine_type_default_args */ a_type_ptr copy_routine_type_with_param_types(a_type_ptr from_type, a_boolean copy_default_args) /* Make a copy of a routine type and its param types list. This routine is called in cases where a routine type and its copy may not share the same param-types list (for example, when as the result of a user error a routine type in a function definition is based on a typedef). If copy_default_args is TRUE, the copies of the default argument expressions are made; otherwise, they are not made and the has_default_arg flag is set to FALSE in the param-type entry of the new type. */ { a_type_qualifier_set qualifiers; a_type_ptr to_type; /* Save any qualifiers above the routine type. These will be put on top of the newly created type later. */ qualifiers = get_type_qualifiers(from_type); from_type = skip_typerefs(from_type); to_type = alloc_type((a_type_kind)tk_routine); copy_type_full(from_type, to_type, copy_default_args); if (qualifiers != TQ_NONE) { /* If the original type had qualifiers above the routine type, add them to the newly created type now. */ to_type = make_qualified_type(to_type, qualifiers); } /* if */ return to_type; } /* copy_routine_type_with_param_types */ a_type_ptr routine_type_without_default_args(a_type_ptr orig_type) /* If the specified routine type has default arguments, return a copy of it with the default arguments removed. Otherwise, return the original type unchanged. */ { a_param_type_ptr ptp, orig_param_type_list; a_type_ptr tp = orig_type; /* Traverse the param-types, stopping as soon as a parameter type with a default argument is encountered. */ orig_param_type_list = skip_typerefs(orig_type)->variant.routine. extra_info->param_type_list; for (ptp = orig_param_type_list; ptp != NULL; ptp = ptp->next) { if (ptp->has_default_arg) { /* A copy of the original type is required, because the default arg expression has to be stripped off. */ tp = alloc_type((a_type_kind)tk_routine); copy_type_full(skip_typerefs(orig_type), tp, /*copy_default_args=*/FALSE); break; } /* if */ } /* for */ return tp; } /* routine_type_without_default_args */ a_type_ptr routine_type_without_param_type_qualifiers(a_type_ptr orig_type) /* orig_type is a routine type. Remove any qualifiers from its parameter type entries. */ { a_type_ptr tp = orig_type; a_param_type_ptr ptp; a_param_type_ptr orig_param_type_list; a_boolean copy_needed = FALSE; /* Traverse the param-types, stopping as soon as a parameter type with qualifiers is encountered. */ orig_type = skip_typerefs(orig_type); orig_param_type_list = orig_type-> variant.routine.extra_info->param_type_list; for (ptp = orig_param_type_list; ptp != NULL; ptp = ptp->next) { if (ptp->qualifiers != TQ_NONE) { /* A copy of the original type is required, because the qualifiers must be removed. */ copy_needed = TRUE; break; } /* if */ } /* for */ if (copy_needed) { /* One or more parameter types have qualifiers. Make a copy of the routine type and the parameter type list. */ tp = alloc_type((a_type_kind)tk_routine); copy_type_full(orig_type, tp, /*copy_default_args=*/FALSE); ptp = tp->variant.routine.extra_info->param_type_list; /* Clear the qualifiers field of the parameter type entries. */ for (; ptp != NULL; ptp = ptp->next) ptp->qualifiers = TQ_NONE; } /* if */ return tp; } /* routine_type_without_param_type_qualifiers */ a_type_ptr routine_type_without_this_class(a_type_ptr orig_type, a_boolean copy_default_args) /* "type" is a routine type. If the type has a this_class, make a copy of the type and clear the this_class information. If a copy is made, copy the default argument information also if copy_default_args is TRUE. Return either the new type or the original type. */ { a_type_ptr type = orig_type; check_assertion(orig_type->kind == (a_type_kind)tk_routine); if (orig_type->variant.routine.extra_info->this_class != NULL) { a_routine_type_supplement_ptr rtsp; type = alloc_type((a_type_kind)tk_routine); copy_type_full(orig_type, type, copy_default_args); rtsp = type->variant.routine.extra_info; rtsp->this_class = NULL; rtsp->has_this_param = FALSE; /* Note that the qualifiers from the original type are retained. */ } /* if */ return type; } /* routine_type_without_this_class */ void ensure_underlying_function_type_is_modifiable(a_type_ptr *p_type, a_type_ptr *func_type) /* The type *p_type should be a function type or a function type under tk_typeref, tk_pointer, and/or tk_ptr_to_member entries. If none of the tk_typeref involved are typedefs or typeof/decltype nodes, leave *p_type unchanged and set *func_type to the underlying function type. Otherwise, create a new function type (returned through func_type) and replace *p_type by an equivalent type that doesn't involve typedefs or typeof/decltype nodes on top of the underlying function type (in some cases, the *p_type entry itself is not replaced, but instead a type entry along the chain of entries from *p_type to the underlying function type is replaced). */ { a_boolean make_new_type = FALSE; a_type_ptr tp = *p_type; /* Go down the chain of type entries looking for typedefs and/or decltype entries. (The typedef-like entry is not modifiable since it is likely used in other contexts, and the chain of pointer and qualifier entries that precede it may be shared through the base_types lists. In such cases, the chain must therefore be copied before the underlying function type is modified.) */ if (tp->kind == (a_type_kind)tk_typeref && is_typeref_kind(tp, trk_for_type_attributes)) { /* The first type entry of the chain is to hold attributes. These are not shared via the based_types list (so it is safe to modify them) and the caller may expect that entry to be preserved even if the underlying chain must be duplicated. */ p_type = &tp->variant.typeref.type; tp = *p_type; } /* if */ do { switch (tp->kind) { case tk_routine: *func_type = tp; break; case tk_pointer: tp = tp->variant.pointer.type; break; case tk_ptr_to_member: tp = tp->variant.ptr_to_member.type; break; case tk_typeref: if (typeref_is_typedef(tp) || typeref_is_type_operator(tp)) { make_new_type = TRUE; } else { tp = tp->variant.typeref.type; } /* if */ break; default: unexpected_condition(); } /* switch */ } while (tp->kind != (a_type_kind)tk_routine && !make_new_type); if (make_new_type) { /* Duplicate the type chain, without duplicating typedefs. */ a_boolean done = FALSE; a_type_ptr *p_entry = p_type; tp = *p_type; while (!done) { a_boolean attrib_only = tp->kind == (a_type_kind)tk_typeref && (typeref_is_typedef(tp) || typeref_is_type_operator(tp)); if (attrib_only) { /* If there is an intervening typedef/decltype/typeof entry, do not copy the entry proper, but record any attributes that it may carry. */ if (tp->source_corresp.attributes != NULL) { *p_entry = make_typeref_with_attributes(tp->variant.typeref.type, tp->source_corresp.attributes); } /* if */ } else { *p_entry = alloc_type(tp->kind); copy_type(tp, *p_entry); } /* if */ /* Move to the next underlying type if appropriate. */ switch (tp->kind) { case tk_routine: *func_type = *p_entry; done = TRUE; break; case tk_pointer: tp = tp->variant.pointer.type; p_entry = &(*p_entry)->variant.pointer.type; break; case tk_ptr_to_member: tp = tp->variant.ptr_to_member.type; p_entry = &(*p_entry)->variant.ptr_to_member.type; break; case tk_typeref: if (!attrib_only || tp->source_corresp.attributes != NULL) { if (*p_entry == tp->variant.typeref.type) { /* If a for_type_attributes typeref follows an initial typedef (that also has attributes), make_typeref_with_attributes above will simply return the for_type_attributes typeref, in which case p_entry already has the correct value. */ } else { /* A new entry was created to match the original tk_typeref entry. */ p_entry = &(*p_entry)->variant.typeref.type; } /* if */ } /* if */ tp = tp->variant.typeref.type; break; default: unexpected_condition(); } /* switch */ } /* while */ } else { *func_type = tp; } /* if */ } /* ensure_underlying_function_type_is_modifiable */ #if GENERATE_SOURCE_SEQUENCE_LISTS #if TEMPLATE_INSTANTIATIONS_IN_SOURCE_SEQUENCE_LISTS #if FRIEND_AND_MEMBER_DEFINITIONS_MAY_BE_MOVED_OUT_OF_CLASS a_boolean class_type_can_be_named_in_namespace_scope(a_type_ptr type) /* Return true if the given class type and all its enclosing class types have a name. */ { a_boolean result; check_assertion(is_class_struct_union_type(type)); type = skip_typerefs(type); result = has_name(type); if (result) { while (type->source_corresp.is_class_member) { type = skip_typerefs(parent_class_of(type)); if (!has_name(type)) { result = FALSE; break; } /* if */ } /* while */ } /* if */ return result; } /* class_type_can_be_named_in_namespace_scope */ #endif /* FRIEND_AND_MEMBER_DEFINITIONS_MAY_BE_MOVED_OUT_OF_CLASS */ #endif /* TEMPLATE_INSTANTIATIONS_IN_SOURCE_SEQUENCE_LISTS */ #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ #endif /* !STANDALONE_UTILITY_PROGRAM */ void skip_start_of_pack_placeholders_simple(a_template_arg_ptr *p_tap) /* *tap points to a start of pack expansion placeholder. Advance to the next real argument. Note that there may be no actual arguments for a pack, and there may be several pack expansion placeholders in a row. Because of this, even though *tap points to a pack expansion placeholder, the argument returned may not be associated with a pack. *tap will be set to NULL when the end of the argument list is reached. See skip_start_of_pack_placeholders in templates.c for a version of this routine that handles the parameter list as well as the argument list. */ { a_template_arg_ptr tap; check_assertion(p_tap != NULL); tap = *p_tap; do { tap = tap->next; } while (tap != NULL && is_start_of_pack_expansion_templ_arg(tap)); *p_tap = tap; } /* skip_start_of_pack_placeholders_simple */ #if !STANDALONE_UTILITY_PROGRAM a_template_arg_ptr copy_template_arg_list(a_template_arg_ptr orig_list) /* Create a copy of the template argument list specified by orig_list and return a pointer to the new list. */ { a_template_arg_ptr new_list = NULL; a_template_arg_ptr tail = NULL; a_template_arg_ptr tap; for (tap = orig_list; tap != NULL; tap = tap->next) { a_template_arg_ptr new_tap; new_tap = alloc_template_arg(tap->kind); *new_tap = *tap; new_tap->next = NULL; if (new_list == NULL) new_list = new_tap; if (tail != NULL) tail->next = new_tap; tail = new_tap; } /* for */ return new_list; } /* copy_template_arg_list */ a_template_arg_ptr copy_template_type_arg_list_with_deduplication( a_template_arg_ptr orig_list) /* Create a copy of the template type argument list specified by orig_list and return a pointer to the new list with duplicate types removed. */ { a_template_arg_ptr result = NULL, *list_tail = &result; for (; orig_list != NULL; orig_list = orig_list->next) { a_type_ptr type; a_boolean is_duplicate = FALSE; check_assertion(is_type_templ_arg(orig_list)); type = orig_list->variant.type; for (a_template_arg_ptr tap = result; !is_duplicate && tap != NULL; tap = tap->next) { is_duplicate = identical_types(tap->variant.type, type); } /* for */ if (!is_duplicate) { a_template_arg_ptr new_tap = alloc_template_arg(orig_list->kind); *new_tap = *orig_list; new_tap->next = NULL; *list_tail = new_tap; list_tail = &new_tap->next; } /* if */ } /* for */ return result; } /* copy_template_type_arg_list_with_deduplication */ a_boolean is_default_constructor(a_routine_ptr rout, a_boolean is_declarative_context) /* Return TRUE if rout points to a default constructor routine entry. is_declarative_context is TRUE if this is a constructor declaration rather than a constructor reference. */ { a_param_type_ptr ptp; a_boolean is_def_ctor = FALSE; a_type_ptr rout_type = skip_typerefs(rout->type); check_assertion(rout_type->kind == (a_type_kind)tk_routine); ptp = rout_type->variant.routine.extra_info->param_type_list; /* There are no parameters or if the first (and therefore its successors, if any) has a default argument expression, then this is a default constructor. */ if (!special_kind_is(rout, sfk_constructor)) { } else if (ptp == NULL) { is_def_ctor = TRUE; } else if (ptp->has_default_arg) { is_def_ctor = TRUE; if (!is_declarative_context && !rout->is_prototype_instantiation) { /* If is_declarative_context is TRUE, the check for a default argument on the first parameter is more relaxed, but when it is FALSE, only return TRUE if the default argument expression has already been generated. This is to handle cases like this: class X { X(X* = new X); }; where it is important *not* to find X(X*) when generating IL for the default argument expression (lest the compiler go into a loop). In other words, the above will produce the same error as this: class X { X(X*); }; X::X(X* = new X) { ... } It's not only the first param-type entry that has to be checked, since there's also this to deal with: class X { X(int = 0, X* = new X); }; A default argument with a NULL default_arg_expr is accepted if it has an unevaluated template value, because we know this value can be produced when the call is generated. */ for (; ptp != NULL;ptp = ptp->next) { if (ptp->default_arg_expr == NULL && !ptp->has_unevaluated_template_default) { is_def_ctor = FALSE; break; } /* if */ } /* for */ } /* if */ } else if (ptp->is_parameter_pack && ptp->next == NULL) { /* A parameter pack can be expanded with zero types to produce a default constructor. */ is_def_ctor = TRUE; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (ptp->is_cli_param_array) { /* A C++/CLI parameter array can take a zero-length array, so a constructor with such a parameter first can be called as a default constructor. */ is_def_ctor = TRUE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ return is_def_ctor; } /* is_default_constructor */ a_boolean is_copy_constructor_type( a_type_ptr routine_type, a_type_ptr class_of_which_a_member, a_type_qualifier_set *qualifiers, a_boolean include_move_ctors, a_boolean is_declarative_context) /* Return TRUE if routine_type (a constructor's function type) is the type of a copy constructor for class class_of_which_a_member; if it is, also set and return *qualifiers to indicate the type qualifiers on the copy constructor's first parameter -- this will show what restrictions are placed on the object being copied. If include_move_ctors is TRUE, also return TRUE if routine_type is the type of a "move constructor" (the first parameter has an rvalue reference type instead of the lvalue reference type of a true copy constructor). is_declarative_context is TRUE if this is a constructor declaration rather than a constructor reference. If qualifiers is NULL, no value is returned for that. */ { a_param_type_ptr ptp; a_type_qualifier_set local_qualifiers; a_boolean is_cctor = FALSE; if (qualifiers == NULL) { qualifiers = &local_qualifiers; } /* if */ /* A constructor is deemed a copy constructor if (1) the type of the first parameter is reference-to-class or reference-to-qualified-class where "class" is the class of which it is a member function, and (2) the function can be called with only one argument. */ ptp = skip_typerefs(routine_type)-> variant.routine.extra_info->param_type_list; /* If the param type entry is non-NULL there is at least one argument. If there is a second argument and it has a default expression, the function call need not explicitly mention the second argument. */ /* An ellipsis is also allowed by virtue of the fact that it is not checked for. */ if (ptp != NULL && (include_move_ctors ? is_any_reference_type(ptp->type) : is_any_lvalue_reference_type(ptp->type)) && (ptp->next == NULL || ptp->next->has_default_arg)) { a_type_ptr tp = type_pointed_to(ptp->type); a_type_ptr unqualified_tp = skip_typerefs(tp); if (identical_types(unqualified_tp, class_of_which_a_member)) { /* It is probably a copy constructor. */ is_cctor = TRUE; #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && is_tracking_reference_type(ptp->type) && !is_managed_class_type(class_of_which_a_member)) { /* A constructor that copies a native class using a tracking reference is not considered a copy constructor, so it won't improperly compete with the real copy constructor. */ is_cctor = FALSE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (!is_declarative_context) { /* If this is a call context, be sure the default argument has already been scanned. Here's a case where this makes a difference: struct X { static X xx; X(const X&, int i = (throw xx, 1)) { } }; In even more obscure cases, there may be more than one parameter to examine. Note: the loop start with the second parameter, if there is one. A default argument with a NULL default_arg_expr is accepted if it has an unevaluated template value, because we know this value can be produced when the call is generated. */ for (ptp = ptp->next; ptp != NULL; ptp = ptp->next) { if (ptp->default_arg_expr == NULL && !ptp->has_unevaluated_template_default) { is_cctor = FALSE; break; } /* if */ } /* for */ } /* if */ if (is_cctor) { /* See if the object being copied is qualified. */ *qualifiers = get_top_level_type_qualifiers(tp); } /* if */ } /* if */ } /* if */ return is_cctor; } /* is_copy_constructor_type */ a_boolean is_copy_constructor(a_routine_ptr ctor_rout, a_type_ptr class_of_which_a_member, a_type_qualifier_set *qualifiers, a_boolean include_move_ctors, a_boolean is_declarative_context) /* Return TRUE if ctor_rout points to a copy constructor routine entry for class_of_which_a_member; if it does, also set and return *qualifiers to indicate the type qualifiers on the copy constructor's first parameter -- this will show what restrictions are placed on the object being copied. is_declarative_context is TRUE if this is a constructor declaration rather than a constructor reference. If include_move_ctors is TRUE, also return TRUE if the given routine is a move constructor for the given class. If class_of_which_a_member is NULL, it is set from ctor_rout. If qualifiers is NULL, no value is returned for that. ctor_rout must be a constructor. */ { a_boolean is_cctor; check_assertion(special_kind_is(ctor_rout, sfk_constructor)); if (class_of_which_a_member == NULL) { class_of_which_a_member = parent_class_of(ctor_rout); } /* if */ is_cctor = is_copy_constructor_type(ctor_rout->type, class_of_which_a_member, qualifiers, include_move_ctors, is_declarative_context); return is_cctor; } /* is_copy_constructor */ a_boolean copy_ctor_is_move_ctor(a_routine_ptr rp) /* The given routine is a copy constructor or a move constructor. Return TRUE if it is a move constructor. */ { a_type_ptr rtp = skip_typerefs(rp->type); a_param_type_ptr ptp = rtp->variant.routine.extra_info->param_type_list; check_assertion(ptp != NULL); return is_rvalue_reference_type(ptp->type); } /* copy_ctor_is_move_ctor */ a_boolean routine_is_move_constructor(a_routine_ptr rp) /* Return TRUE if and only if the given routine is a move constructor. */ { a_type_qualifier_set qualifiers; return special_kind_is(rp, sfk_constructor) && rvalue_references_enabled && /* For speed. */ is_copy_constructor(rp, parent_class_of(rp), &qualifiers, /*include_move_ctors=*/TRUE, /*is_declarative_context=*/TRUE) && copy_ctor_is_move_ctor(rp); } /* routine_is_move_constructor */ a_boolean is_copy_assignment_operator_type( a_type_ptr routine_type, a_type_ptr class_type, a_boolean move_assign_okay, a_boolean *is_ref_arg, a_type_qualifier_set *qualifiers, a_boolean *is_base_class_match) /* Return TRUE if routine_type (an operator= function's type) is the type of a copy assignment operator for class class_type; if it is, also (if is_ref_arg is non-NULL) set *is_ref_arg to indicate whether the non-object parameter has a reference type, and (if qualifiers is non-NULL) set *qualifiers to indicate the type qualifiers on that parameter. If move_assign_okay is TRUE, also return TRUE if routine_type is the type of a "move assignment operator" (the non-object parameter has an rvalue reference type). If is_base_class_match is non-NULL, return *is_base_class_match for the cfront compatibility case where the copy assignment operator takes a base class as its input. An explicit "this" parameter is not counted as the source parameter. */ { a_boolean is_copy_assign = FALSE; a_param_type_ptr ptp; a_type_ptr tp; if (is_ref_arg != NULL) *is_ref_arg = FALSE; if (qualifiers != NULL) *qualifiers = TQ_NONE; if (is_base_class_match != NULL) *is_base_class_match = FALSE; class_type = skip_typerefs(class_type); check_assertion(is_immediate_class_type(class_type)); routine_type = skip_typerefs(routine_type); check_assertion(routine_type->kind == (a_type_kind)tk_routine); ptp = first_nonobject_param(routine_type); if (ptp != NULL) { tp = skip_typerefs(ptp->type); if (move_assign_okay ? is_reference_type(tp) : is_lvalue_reference_type(tp)) { /* Reference argument. */ tp = type_pointed_to(tp); /* Don't do a skip_typerefs on what's returned from type_pointed_to, since we need to distinguish between "A&" and "const A&". */ if (is_ref_arg != NULL) *is_ref_arg = TRUE; } /* if */ if (is_class_struct_union_type(tp)) { /* The type of the non-object parameter is a class type. */ if (types_are_compatible_ignoring_qualifiers(tp, class_type)) { /* The parameter's type matches the class of which the assignment operator is a member. */ is_copy_assign = TRUE; } else if (allow_copy_assignment_op_with_base_class_param) { if (find_base_class_of(class_type, tp) != NULL) { /* The parameter's type matches a base class of the class of which the assignment operator is a member. */ is_copy_assign = TRUE; if (is_base_class_match != NULL) *is_base_class_match = TRUE; } /* if */ } /* if */ if (is_copy_assign && qualifiers != NULL) { /* Check the qualifiers. (Call get_top_level_type_qualifiers instead of get_type_qualifiers because we know tp cannot be an array.) */ *qualifiers = get_top_level_type_qualifiers(tp); } /* if */ } /* if */ } /* if */ return is_copy_assign; } /* is_copy_assignment_operator_type */ a_boolean routine_is_copy_or_move_assign_operator( a_routine_ptr rp, a_type_qualifier_set *tqs, a_boolean *is_move) /* Return TRUE if the given function is a copy or move assignment operator. If it is, set *tqs to the qualifiers referred to by the non-object parameter (or TQ_NONE if that parameter is not a reference), and set *is_move to TRUE if it is a move assignment operator and to FALSE otherwise. Otherwise, set *tqs to TQ_NONE and *is_move to FALSE. */ { a_boolean result, base_match_only; result = special_kind_is(rp, sfk_operator) && rp->variant.opname_kind == (an_opname_kind)onk_assign && is_copy_assignment_operator_type(rp->type, parent_class_of(rp), /*move_assign_okay=*/TRUE, /*is_ref_arg=*/(a_boolean *)NULL, tqs, &base_match_only) && !base_match_only; if (result) { *is_move = is_rvalue_reference_type( first_nonobject_param(skip_typerefs(rp->type))->type); } else { *is_move = FALSE; } /* if */ return result; } /* routine_is_copy_or_move_assign_operator */ a_boolean routine_is_move_assignment_operator(a_routine_ptr rp) /* Return TRUE if and only if the given routine is a move assignment operator. */ { a_type_qualifier_set tqs; a_boolean is_move; return routine_is_copy_or_move_assign_operator(rp, &tqs, &is_move) && is_move; } /* routine_is_move_assignment_operator */ #if DO_IL_LOWERING a_boolean special_member_is_user_provided(a_routine_ptr rp) /* Return TRUE if the given special member function is "user-provided"; i.e., user-declared, and not defaulted on its first declaration. */ { a_boolean result = !rp->compiler_generated; check_assertion(rp->source_corresp.is_class_member && !special_kind_is(rp, sfk_none)); if (result && rp->is_defaulted) { /* Check that the member function is not defaulted on its first declaration. */ result = rp->defined_outside_of_parent; } /* if */ return result; } /* special_member_is_user_provided */ #endif /* DO_IL_LOWERING */ a_boolean is_main_function(a_routine_ptr routine) /* Return TRUE if and only if the given entry describes a global scope "main" routine. */ { a_boolean result = FALSE; a_source_correspondence_ptr scp = &routine->source_corresp; a_symbol_ptr sym = (a_symbol_ptr)scp->assoc_info; if (sym != NULL && !scp_is_class_or_namespace_member(scp) && sym->header->has_intrinsic_name) { result = (strcmp(sym->header->identifier, "main") == 0); } /* if */ return result; } /* is_main_function */ static void instantiate_il_entity(a_source_correspondence *scp) /* Call set_instance_required on the IL entity with the indicated source correspondence. That will cause it to be instantiated if it is a template function or static data member (or, in some configurations, an inline function). */ { a_symbol_ptr sym = (a_symbol_ptr)(scp->assoc_info); if (sym != NULL && /* Don't kick off instantiations while in a prototype instantiation. */ !is_template_dependent_context()) { if (sym->kind == (a_symbol_kind)sk_static_data_member || sym->kind == (a_symbol_kind)sk_variable || sym->kind == (a_symbol_kind)sk_member_function || sym->kind == (a_symbol_kind)sk_routine) { set_instance_required(sym, TRUE, SIR_NONE); } /* if */ } /* if */ } /* instantiate_il_entity */ static void do_instantiations_for_copied_default_arg_expr( an_expr_node_ptr expr) /* expr is an expression that is a copy of a default argument expression, i.e., it is a real use of the default argument expression. Instantiate any template entities referenced in the top level of the expression, because they were not instantiated during the original expression scan. */ { if (expr->kind == (an_expr_node_kind)enk_routine) { instantiate_il_entity(&node_routine(expr)->source_corresp); } else if (expr->kind == (an_expr_node_kind)enk_variable) { instantiate_il_entity(&node_variable(expr)->source_corresp); } else if (expr->kind == (an_expr_node_kind)enk_constant) { a_constant_ptr con = node_constant(expr); if (constant_is(con, ck_address)) { if (con->variant.address.kind == (an_address_base_kind)abk_routine) { instantiate_il_entity( &con->variant.address.variant.routine->source_corresp); } else if (con->variant.address.kind == (an_address_base_kind)abk_variable) { instantiate_il_entity( &con->variant.address.variant.variable->source_corresp); } /* if */ } else if (constant_is(con, ck_ptr_to_member)) { if (con->variant.ptr_to_member.is_function_ptr) { a_routine_ptr rout = con->variant.ptr_to_member.variant.routine; if (rout != NULL) { instantiate_il_entity(&rout->source_corresp); } /* if */ } /* if */ } /* if */ } else if (expr->kind == (an_expr_node_kind)enk_new_delete) { a_new_delete_supplement_ptr ndsp = expr->variant.new_delete; if (ndsp->routine != NULL) { instantiate_il_entity(&ndsp->routine->source_corresp); } /* if */ } /* if */ } /* do_instantiations_for_copied_default_arg_expr */ void set_dynamic_init_constant(a_dynamic_init_ptr dip, a_constant *constant) /* Set the constant variant of the indicated dynamic init entry to point to "constant". The dynamic init kind is already set. This exists so that the partially-initialized flag can be copied into the dynamic init. */ { check_assertion(dip != NULL && (dip->kind==(a_dynamic_init_kind)dik_constant || dip->kind==(a_dynamic_init_kind)dik_nonconstant_aggregate || dip->kind==(a_dynamic_init_kind)dik_lambda)); dip->variant.constant.ptr = constant; dip->is_partially_initialized = constant->is_partially_initialized; if (constant->explicit_cast_applied) { dip->is_explicit_cast = TRUE; } /* if */ if (constant->explicit_braces_on_aggregate) { dip->is_braced_initializer = TRUE; } /* if */ if (constant->is_compound_literal) { dip->is_compound_literal = TRUE; } /* if */ } /* set_dynamic_init_constant */ void add_to_dynamic_inits_list(a_dynamic_init_ptr dip) /* Add the given dynamic initialization entry to the file-scope dynamic_inits list. */ { a_scope_stack_entry_ptr ssep; a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; /* Only the file scope has a dynamic-inits list -- dynamic init entries generated for namespace scopes go on the file scope list, and in function and block scopes dynamic initialization is handled by statements. */ ssep = &scope_stack[DEPTH_OF_FILE_SCOPE]; pointers_block = assoc_pointers_block_of(ssep); sp = ssep->il_scope; if (sp->dynamic_inits == NULL) { sp->dynamic_inits = dip; } else { pointers_block->last_dynamic_init->next = dip; } /* if */ pointers_block->last_dynamic_init = dip; dip->next = NULL; } /* add_to_dynamic_inits_list */ static an_object_lifetime_ptr init_expr_lifetime_of(a_dynamic_init_ptr dip) /* Given a dynamic init entry, return a (possibly NULL) pointer to an object lifetime representing the full-expression lifetime that is the initialization. (This may be given directly by the init_expr_lifetime field or indirectly, if this is a dik_expression dynamic init entry.) */ { an_object_lifetime_ptr olp = NULL; if (dip != NULL) { olp = dip->init_expr_lifetime; if (olp == NULL) { if (dip->kind == (a_dynamic_init_kind)dik_expression && dip->variant.expression->kind == (an_expr_node_kind)enk_object_lifetime) { /* An enk_object_lifetime node will always be the top-most node if a lifetime was pushed for the full-expression. */ olp = dip->variant.expression->variant.object_lifetime.ptr; } /* if */ } /* if */ } /* if */ return olp; } /* init_expr_lifetime_of */ /* Forward declarations needed because of recursion: */ static void unlink_object_lifetime(an_object_lifetime_ptr lifetime); static a_lambda_ptr copy_lambda(a_lambda_ptr lambda, an_expr_copy_options_set options, a_tree_copy_control_block *cblock); static a_dynamic_init_ptr i_copy_dynamic_init( a_dynamic_init_ptr dip, an_expr_copy_options_set options, a_tree_copy_control_block *cblock) /* Make a copy of a dynamic initialization entry and return a pointer to the copy. This is not a general-purpose routine -- it is meant to be called from copy_expr_tree for the kinds of dynamic initializations done under an expression node (the "i_" prefix means "internal"). options is a set of options for the copy. cblock is a control block for the copy. */ { a_dynamic_init_ptr new_dip; a_boolean need_object_lifetime_pop = FALSE; an_object_lifetime_ptr init_expr_lifetime; a_boolean saved_overlaps_temps_in_inner_lifetime; new_dip = alloc_dynamic_init(dip->kind); *new_dip = *dip; if (dip->class_rvalue_initialized_through_master_entry || dip->is_reused_value) { /* Record the original and copy addresses for dynamic inits that might be referenced elsewhere in the copied tree, so we can get the proper remapped address later. */ add_copy_remap_entry((char *)dip, (char *)new_dip, cblock); } /* if */ /* In the absence of the CE_INSIDE_CONDITIONAL_EXPRESSION option, the value of inside_conditional_expression is copied. */ if (options & CE_INSIDE_CONDITIONAL_EXPRESSION) { new_dip->inside_conditional_expression = TRUE; } /* if */ #if MINIMAL_INLINING if (options & CE_DOING_INLINING_OF_FUNCTION_CALL) { /* Look for variables that get remapped while copying the expressions in a function being inlined. */ if (dip->variable != NULL) { new_dip->variable = remap_var_for_inlining(dip->variable); } /* if */ } /* if */ #endif /* MINIMAL_INLINING */ new_dip->overlaps_temps_in_inner_lifetime = FALSE; new_dip->lifetime_of_overlapping_temps = NULL; /* Save the initial value of overlaps_temps_in_inner_lifetime; this may change when remove_from_destruction_list is called later. */ saved_overlaps_temps_in_inner_lifetime = dip->overlaps_temps_in_inner_lifetime; new_dip->init_expr_lifetime = NULL; /* Push an object lifetime if copying a dynamic initializer that has an init_expr_lifetime and there's not already an expression lifetime on the object lifetime stack. */ init_expr_lifetime = dip->init_expr_lifetime; if (init_expr_lifetime != NULL && curr_object_lifetime->kind != (an_object_lifetime_kind)olk_expr_temporary) { push_object_lifetime(iek_dynamic_init, (char *)new_dip, init_expr_lifetime->kind); need_object_lifetime_pop = TRUE; } /* if */ #if DO_IL_LOWERING new_dip->destructible_entity_descr = NULL; #endif /* DO_IL_LOWERING */ switch (dip->kind) { case dik_none: case dik_zero: break; case dik_expression: case dik_class_result_via_ctor: new_dip->variant.expression = i_copy_expr_tree(dip->variant.expression, options, cblock); break; case dik_constructor: if (options & CE_COPYING_EVALUATED_DEFAULT_ARG_EXPR) { /* Instantiate referenced routines in a default argument expression. */ /* Watch out for prototype instantiations. */ if (dip->variant.constructor.ptr != NULL) { instantiate_il_entity(&dip->variant.constructor.ptr->source_corresp); } /* if */ } /* if */ new_dip->variant.constructor.args = i_copy_list_of_expr_trees(dip->variant.constructor.args, options, cblock); break; case dik_lambda: case dik_constant: case dik_nonconstant_aggregate: { an_expr_copy_options_set options_unshared; /* The constant pointed to is unshared and must be copied. */ options_unshared = (options & ~(an_expr_copy_options_set) CE_COPIED_CONSTANTS_MAY_BE_SHARED); new_dip->variant.constant.ptr = i_copy_constant_full(dip->variant.constant.ptr, (a_constant *)NULL, options_unshared, cblock); if (dip->variant.constant.lambda != NULL) { new_dip->variant.constant.lambda = copy_lambda(dip->variant.constant.lambda, options_unshared, cblock); } /* if */ } break; case dik_bitwise_copy: if (dip->variant.bitwise_copy.source != NULL) { new_dip->variant.expression = i_copy_expr_tree(dip->variant.bitwise_copy.source, options, cblock); } /* if */ break; default: unexpected_condition_str("i_copy_dynamic_init: bad kind"); } /* switch */ /* Pop an init_expr_lifetime object lifetime if we had pushed one previously. */ if (need_object_lifetime_pop) { (void)pop_object_lifetime(); } /* if */ if (dip->lifetime != NULL || options & CE_COPYING_DMI_DIP) { /* This dynamic init is on a destruction list (or will be because it is a data member initializer -- which have no lifetimes in the IL and is being copied into a constructor's scope), so the copy must be put on a destruction list in the current context. */ new_dip->lifetime = NULL; new_dip->next_in_destruction_list = NULL; if (options & CE_COPY_NOT_EVALUATED) { /* Since the copied expression is not evaluated, no object lifetime entry is required. */ } else if ((scope_stack_top().in_template_arg_list || options & CE_COPYING_FOR_CONSTEXPR_FOLDING) #if DO_IL_LOWERING && !il_lowering_underway #endif /* DO_IL_LOWERING */ ) { /* Object lifetime representations aren't considered during constant expression evaluation (which is a compile-time evaluation). */ check_assertion_or_expect_error( options & CE_COPYING_DEFAULT_MEMBER_INIT); } else { a_boolean static_lifetime = FALSE; if (dip->lifetime != NULL) { static_lifetime = is_static_object_lifetime_kind(dip->lifetime->kind); } /* if */ if (dip->destruction_is_for_partially_constructed_aggregate) { static_lifetime = FALSE; } /* if */ record_end_of_lifetime_destruction(new_dip, static_lifetime, /*block_lifetime=*/FALSE); if (saved_overlaps_temps_in_inner_lifetime) { /* The original entry had this flag set. We need to re-establish the overlapping lifetime relationship in the copy. */ an_object_lifetime_ptr overlapped_olp = init_expr_lifetime_of(new_dip); if (overlapped_olp != NULL) { overlapped_olp->parent_destruction_sublist = new_dip; new_dip->overlaps_temps_in_inner_lifetime = TRUE; new_dip->lifetime_of_overlapping_temps = overlapped_olp; } else { /* There is no lifetime (yet) with which to establish the lifetime relationship. */ check_assertion(!new_dip->overlaps_temps_in_inner_lifetime); } /* if */ } /* if */ } /* if */ if (options & CE_COPYING_EVALUATED_DEFAULT_ARG_EXPR) { /* Instantiate the destructor. */ check_assertion(dip->destructor != NULL); /* Not expecting VLAs. */ instantiate_il_entity(&dip->destructor->source_corresp); } /* if */ } /* if */ #if DO_IL_LOWERING if (options & CE_UNLINK_SOURCE_DESTRUCTIONS) { /* Unlink the source dynamic initialization from its lifetime. This is requested by the caller when the source expression will not remain in the IL tree. */ new_dip->destructible_entity_descr = dip->destructible_entity_descr; dip->destructible_entity_descr = NULL; if (init_expr_lifetime != NULL) { unlink_object_lifetime(init_expr_lifetime); } /* if */ remove_from_destruction_list(dip); } /* if */ if (options & CE_TRANSFER_DESTR_ENTITY_DESCR) { /* If IL lowering has already attached a destructible entity description to the dynamic initialization, it goes with the copy and not the original. (If the pointer in the original were not cleared, the destructible entity description would be freed twice.) */ dip->destructible_entity_descr = NULL; } else { /* The destructible entity description stays with the original entry. */ new_dip->destructible_entity_descr = NULL; } /* if */ #endif /* DO_IL_LOWERING */ if (dip->master_entry != NULL) { /* Change the master_entry pointer to the copy if that entry was copied in the current copy. If it wasn't copied, leave the original address. */ char *copy = find_copy_remap_address((char *)dip->master_entry, cblock); if (copy != NULL) new_dip->master_entry = (a_dynamic_init_ptr)copy; } /* if */ return new_dip; } /* i_copy_dynamic_init */ a_dynamic_init_ptr copy_dynamic_init(a_dynamic_init_ptr dip, an_expr_copy_options_set options) /* Make a copy of a dynamic initialization (and everything under it) and return a pointer to it. options is a set of options for the copy. */ { a_tree_copy_control_block cblock; a_dynamic_init_ptr dip_copy; /* This is a wrapper around i_copy_dynamic_init that initializes the control block to be used for the entire copy. */ clear_tree_copy_control_block(&cblock); dip_copy = i_copy_dynamic_init(dip, options, &cblock); done_with_tree_copy_control_block(&cblock); return dip_copy; } /* copy_dynamic_init */ a_local_static_variable_init_ptr make_local_static_variable_init( a_variable_ptr var, a_scope_ptr var_scope, an_init_kind init_kind, a_constant_ptr con, a_dynamic_init_ptr dip) /* Allocate a_local_static_variable_init entry for the indicated variable (which is a member of the scope var_scope), set its kind to init_kind, and use con or dip as appropriate to set one of the variant fields. Add the entry to the list for the scope var_scope. Set the initialization kind for the variable to initk_function_local. Return a pointer to the entry. If var_scope is NULL, use the current scope in the scope stack. */ { a_local_static_variable_init_ptr lsvip; db_enter(5, "make_local_static_variable_init"); if (var_scope == NULL) { var_scope = ensure_il_scope_exists(&scope_stack[decl_scope_level]); check_assertion(var_scope != NULL); } /* if */ check_assertion(is_local_scope_kind(var_scope->kind)); check_assertion(curr_il_region_number != file_scope_region_number); lsvip = alloc_local_static_variable_init(); lsvip->next = var_scope->local_static_variable_inits; var_scope->local_static_variable_inits = lsvip; lsvip->variable = var; var->init_kind = (an_init_kind)initk_function_local; lsvip->init_kind = init_kind; switch (init_kind) { case initk_static: lsvip->initializer.constant = con; break; case initk_dynamic: lsvip->initializer.dynamic = dip; break; default: unexpected_condition_str( "make_local_static_variable_init: bad init kind"); } /* switch */ db_exit(); return lsvip; } /* make_local_static_variable_init */ static a_scope_ptr find_scope_of_variable(a_variable_ptr variable, a_scope_ptr scope) /* If the indicated variable is declared in the indicated scope (a function, block, or local class scope) or one of its sub-scopes, return the appropriate scope. Otherwise, return NULL. */ { a_scope_ptr result_scope = NULL, subscope; a_variable_ptr test_var; a_type_ptr test_type; if (var_has_static_or_thread_storage_duration(variable)) { /* Check for static data members only in class scopes. */ if ((scope->kind == (a_scope_kind)sck_class_struct_union) == variable->source_corresp.is_class_member) { /* Check static variables. */ for (test_var = scope->variables; test_var != NULL; test_var = test_var->next) { if (test_var == variable) { result_scope = scope; goto end_of_routine; } /* if */ } /* for */ } /* if */ } else { /* Check auto variables. */ for (test_var = scope->nonstatic_variables; test_var != NULL; test_var = test_var->next) { if (test_var == variable) { result_scope = scope; goto end_of_routine; } /* if */ } /* for */ } /* if */ if (variable->source_corresp.is_class_member) { /* Look for static data members. */ for (test_type = scope->types; test_type != NULL; test_type = test_type->next) { if (is_immediate_class_type(test_type)) { subscope=test_type->variant.class_struct_union.extra_info->assoc_scope; if (!scope_is_null_or_placeholder(subscope)) { result_scope = find_scope_of_variable(variable, subscope); if (result_scope != NULL) goto end_of_routine; } /* if */ } /* if */ } /* for */ } /* if */ /* Check sub-scopes. */ for (subscope = scope->scopes; subscope != NULL; subscope = subscope->next) { result_scope = find_scope_of_variable(variable, subscope); if (result_scope != NULL) goto end_of_routine; } /* for */ end_of_routine: return result_scope; } /* find_scope_of_variable */ static a_scope_ptr find_scope_of_type(a_type_ptr type, a_scope_ptr scope) /* If the indicated type is declared in the indicated scope (a function, block, or local class scope) or one of its sub-scopes, return the appropriate scope. Otherwise, return NULL. */ { a_scope_ptr result_scope = NULL, subscope; a_type_ptr test_type; for (test_type = scope->types; test_type != NULL; test_type = test_type->next) { if (test_type == type) { result_scope = scope; goto end_of_routine; } /* if */ if (!C_mode() && is_immediate_class_type(test_type)) { subscope = test_type->variant.class_struct_union.extra_info->assoc_scope; if (!scope_is_null_or_placeholder(subscope)) { result_scope = find_scope_of_type(type, subscope); if (result_scope != NULL) goto end_of_routine; } /* if */ } /* if */ } /* for */ /* Check sub-scopes. */ for (subscope = scope->scopes; subscope != NULL; subscope = subscope->next) { result_scope = find_scope_of_type(type, subscope); if (result_scope != NULL) goto end_of_routine; } /* for */ end_of_routine: return result_scope; } /* find_scope_of_type */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_local_static_variable_init_ptr find_local_static_variable_init( a_variable_ptr var, a_scope_ptr scope) /* Return a pointer to the local static variable init entry that appears on the linked list for the specified scope or a containing scope and points to the specified variable. (It is an internal error for none to be found.) */ { a_local_static_variable_init_ptr lsvip = NULL; check_assertion(scope != NULL && is_local_scope_kind(scope->kind)); for (; lsvip == NULL && scope != NULL; scope = scope->parent) { for (lsvip = scope->local_static_variable_inits; lsvip != NULL; lsvip = lsvip->next) { if (lsvip->variable == var) { /* Found it. */ break; } /* if */ } /* for */ } /* for */ check_assertion_str2(lsvip != NULL, "find_local_static_variable_init:", "none found for specified variable and scope"); return lsvip; } /* find_local_static_variable_init */ void get_variable_initializer(a_variable_ptr variable, a_scope_ptr var_scope, an_init_kind *p_init_kind, an_initializer_ptr *p_initializer) /* Fetch the initialization kind and initializer pointer for the indicated variable (which is a member of the scope var_scope), and return them in *p_init_kind and *ip_nitializer. This is useful for local static variables, where the initialization information may be provided remotely in a local-static-variable-init entry to sidestep memory region problems (the variable is in the file scope memory region, but the initialization is in the function scope). var_scope can be NULL if it is not known; it will be determined by this routine. We must be in a context where innermost_function_scope is set to indicate the current function, or the variable's scope must be on the scope stack. This function also instantiates in-class initializers of static data members if needed (in GNU C++ mode, where such initializers are instantiated on demand). */ { an_init_kind init_kind = variable->init_kind; an_initializer_ptr initializer = &variable->initializer; if (init_kind == (an_init_kind)initk_function_local) { /* This is a local static variable whose initialization is described by an entry of type a_local_static_variable_init. */ a_local_static_variable_init_ptr lsvip; #if !STANDALONE_UTILITY_PROGRAM if (var_scope == NULL) { /* Determine the scope of the variable. */ var_scope = get_parent_scope_of(variable); } /* if */ #endif /* !STANDALONE_UTILITY_PROGRAM */ lsvip = find_local_static_variable_init(variable, var_scope); init_kind = lsvip->init_kind; initializer = &lsvip->initializer; } else { initializer = &variable->initializer; } /* if */ *p_init_kind = init_kind; *p_initializer = initializer; } /* get_variable_initializer */ #if !STANDALONE_UTILITY_PROGRAM static a_boolean is_cpp26_local_address_constant(a_constant *con) /* Return TRUE if the given constant represents a valid address of a local constant according to P2686R4 (which is slated for inclusion in C++26). */ { a_boolean result = FALSE; if (constant_is(con, ck_address) && cpp26_mode) { if (address_base_is(con, abk_variable)) { a_variable *vp = con->variant.address.variant.variable; if (vp->storage_class == sc_auto && !vp->is_this_parameter && vp->init_kind != initk_binding) { result = TRUE; } /* if */ } /* if */ } /* if */ return result; } /* is_cpp26_local_address_constant */ static a_constant_ptr fold_dynamic_var_init_if_possible( a_dynamic_init_ptr dip, a_type_ptr dest_type) /* If the given dynamic initialization entry can be folded to a constant, return a pointer to a constant entry representing the folded value. Otherwise, return NULL. dest_type is the type being initialized. If dip points to an entry in file-scope memory, the returned constant (if any) is also allocated in file-scope memory; otherwise, it is allocated in the current memory region. */ { a_constant_ptr result = NULL; if (dyn_init_is(dip, dik_constant)) { result = dip->variant.constant.ptr; } else if (constexpr_enabled && !is_template_dependent_type(dest_type)) { a_constant_ptr folded_value = local_constant(); a_diag_list diag_list; a_variable_ptr var = dip->variable; an_init_kind init_kind = initk_none; a_memory_region_number region_to_switch_back_to = NULL_region_number; if (var != NULL) { /* Temporarily set the variable as uninitialized to avoid runaway recursion. */ init_kind = var->init_kind; var->init_kind = initk_none; } /* if */ if (in_file_scope(dip) && curr_il_region_number != file_scope_region_number) { switch_to_file_scope_region(®ion_to_switch_back_to); } /* if */ clear_diag_list(&diag_list); /* Interpret the dynamic initialization into a constant if possible. If the resulting constant is the address of a temporary, keep the dynamic initialization (otherwise, we may fold uses of references to a mutable temporary later on). If the constant is the address of a local variable (checked with is_static_init_constant) ignore the resulting constant since it is not really "constant"; a change is expected in C++26 (via P2686R4) to permit local variable cases also. */ if (interpret_dynamic_init(dip, &pos_curr_token, dest_type, /*is_constant_evaluated=*/TRUE, folded_value, &diag_list) && !(constant_is(folded_value, ck_address) && address_base_is(folded_value, abk_temporary)) && (is_static_init_constant(folded_value) || is_cpp26_local_address_constant(folded_value))) { /* Allocate a new entry, because the original entry may be pointed to indirectly by folded_value. */ result = move_local_constant_to_il(&folded_value); } /* if */ discard_more_info_list(&diag_list); if (folded_value != NULL) { /* If folded_value was not moved to the IL above, release it now. */ release_local_constant(&folded_value); } /* if */ if (region_to_switch_back_to != NULL_region_number) { switch_back_to_original_region(region_to_switch_back_to); } /* if */ if (var != NULL) { var->init_kind = init_kind; } /* if */ } /* if */ return result; } /* fold_dynamic_var_init_if_possible */ a_constant_ptr template_arg_operand_constant(a_template_arg *tap) /* *tap is a nontype template argument represented through an_arg_operand. If that operand embeds an a_constant entry, return a locally-allocated copy of that entry (the called is responsible for freeing it). Otherwise, return NULL. */ { a_constant *result = NULL; an_operand *opnd; check_assertion(tap->arg_operand != NULL && tap->variant.constant == NULL); opnd = &tap->arg_operand->operand; if (is_constant_operand(opnd)) { result = local_constant(); copy_constant(&opnd->variant.constant, result); } /* if */ return result; } /* template_arg_operand_constant */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_constant_ptr initializer_constant(a_variable_ptr var) /* If var is initialized with a constant expression, return the initializer constant; otherwise, return NULL. */ { a_constant_ptr con_val = NULL; an_init_kind init_kind; an_initializer_ptr init; /* Get the initializer and check if it is a constant. */ get_variable_initializer(var, (a_scope_ptr)NULL, &init_kind, &init); if (init_kind == initk_static) { /* The variable has a constant initial value. */ con_val = init->constant; } else if (init_kind == initk_dynamic) { #if !STANDALONE_UTILITY_PROGRAM con_val = fold_dynamic_var_init_if_possible(init->dynamic, var->type); #endif /* !STANDALONE_UTILITY_PROGRAM */ if (con_val != NULL) { /* We already established the constant value. */ #if !STANDALONE_UTILITY_PROGRAM } else if (var->is_template_variable || is_template_dependent_context()) { /* Check for a dependent initialization that might be constant in an instantiation. */ an_expr_node_ptr expr = NULL; if (dyn_init_is(init->dynamic, dik_expression)) { /* Check for a dependent expression that might be a constant in an instantiation. */ expr = init->dynamic->variant.expression; if (expr_is_instantiation_dependent(expr) && !has_statement_expression(expr) && init->dynamic->init_expr_lifetime == NULL) { /* Use the expression as a template parameter constant. */ } else { /* Not a potential constant expression. */ expr = NULL; } /* if */ } else if (dyn_init_is(init->dynamic, dik_constructor) && is_template_param_or_nonreal_class_type(var->type)) { /* A constructor call to a dependent type. Create an enk_temp_init node referring to this dynamic initializer and use that as the expression for a template parameter constant. */ expr = alloc_temp_init_node(skip_typerefs(var->type), init->dynamic, /*is_lvalue=*/FALSE, /*is_explicit_cast=*/FALSE); } /* if */ if (expr != NULL) { /* Make a constant that refers to the dependent expression. */ a_constant_ptr con = local_constant(); make_template_param_expr_constant(expr, con); con_val = copy_unshared_constant(con); release_local_constant(&con); } /* if */ #endif /* !STANDALONE_UTILITY_PROGRAM */ } /* if */ } else if (init_kind == initk_binding) { /* Bindings cannot currently produce constant values. */ } /* if */ return con_val; } /* initializer_constant */ #if !STANDALONE_UTILITY_PROGRAM a_vla_dimension_ptr make_vla_dimension(a_type_ptr array_type, an_expr_node_ptr expr_node, a_boolean in_prototype_scope, a_source_position *position) /* Allocate a_vla_dimension entry for the indicated array_type and set its fields to the values passed in as parameters. Add the entry to the list for the current scope. Return a pointer to the entry. */ { a_scope_stack_entry_ptr ssep; a_scope_ptr il_scope; a_vla_dimension_ptr vdp, end_of_list; db_enter(5, "make_vla_dimension"); check_assertion(depth_innermost_function_scope != NO_SCOPE_DEPTH && scope_stack[decl_scope_level].kind != (a_scope_kind)sck_func_prototype); check_assertion(array_type != NULL && array_type->kind == (a_type_kind)tk_array); /* VLA in block or function scope. Allocate the vla_dimension in the current IL memory region and add it to the vla_dimensions list in the IL scope for the function. */ ssep = &scope_stack[depth_innermost_function_scope]; il_scope = ensure_il_scope_exists(ssep); check_assertion_str(il_scope != NULL, "make_vla_dimension: NULL IL scope"); /* Allocate and initialize the vla_dimension. */ vdp = alloc_vla_dimension(); vdp->type = array_type; vdp->dimension_expr = expr_node; vdp->in_prototype_scope = in_prototype_scope; vdp->position = *position; array_type->variant.array.has_assoc_vla_dimension = TRUE; /* Add the vla_dimension to the end of the list. */ if (il_scope->vla_dimensions == NULL) { il_scope->vla_dimensions = vdp; } else { end_of_list = il_scope->vla_dimensions; while (end_of_list->next != NULL) end_of_list = end_of_list->next; end_of_list->next = vdp; } /* if */ db_exit(); return vdp; } /* make_vla_dimension */ void remove_from_variables_list(a_variable_ptr var_ptr, a_scope_depth scope_depth) /* Unlink the given variable from the variables list for the scope indicated by scope_depth. This is done so the variable can be added again at the end of the list, to keep the variables in order of appearance of their definitions. When scope_level is NO_SCOPE_DEPTH, the scope is computed rather than determined directly. */ { a_variable_ptr prev_var, vp; a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; check_assertion_str(!var_ptr->source_corresp.is_class_member, "remove_from_variables_list: class member not expected"); /* Get a pointer to the scope in which the variable is declared. */ sp = get_scope_for_list(scope_depth, &var_ptr->source_corresp, &pointers_block); check_assertion_str(sp != NULL, "remove_from_variables_list: NULL IL scope"); /* Find the variable on the current list that precedes var_ptr; we'll need it to unlink var_ptr. */ prev_var = NULL; for (vp = sp->variables; vp != NULL; vp = vp->next) { if (vp == var_ptr) break; prev_var = vp; } /* for */ check_assertion_str(vp != NULL, "remove_from_variables_list: not found"); /* Link the previous entry to the entry following this one. */ if (prev_var == NULL) { sp->variables = var_ptr->next; } else { prev_var->next = var_ptr->next; } /* if */ /* If the entry being removed was the last on the list, update the last_variable pointer. */ if (pointers_block != NULL && var_ptr == pointers_block->last_variable) { pointers_block->last_variable = prev_var; } /* if */ } /* remove_from_variables_list */ void add_to_variables_list(a_variable_ptr var_ptr, a_scope_depth scope_depth) /* Add the given variable to the variables list for the scope at the indicated scope depth. */ { a_scope_stack_entry_ptr ssep = NULL; a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; a_boolean at_namespace_or_class_scope; sp = get_scope_for_list(scope_depth, &var_ptr->source_corresp, &pointers_block); check_assertion_str(sp != NULL, "add_to_variables_list: NULL IL scope"); at_namespace_or_class_scope = scope_is(sp, sck_file) || scope_is(sp, sck_namespace) || scope_is(sp, sck_class_struct_union) || scope_is(sp, sck_class_reactivation); if (at_namespace_or_class_scope || scope_depth == NO_SCOPE_DEPTH) { /* If get_scope_for_list returned the file scope and scope_depth is NO_SCOPE_DEPTH, use the depth of the file scope. */ scope_depth = DEPTH_OF_FILE_SCOPE; } /* if */ /* If the specified scope depth is on the stack, get a pointer to the scope stack entry. The only case in which the specified depth is permitted to exceed the current depth of the scope stack is when the depth specifies the file scope (because in such cases, we can get the necessary information elsewhere). */ if (scope_depth != NO_SCOPE_DEPTH && scope_depth <= depth_scope_stack) { assert_is_valid_scope_depth(scope_depth); ssep = &scope_stack[scope_depth]; } /* if */ check_assertion(at_namespace_or_class_scope || ssep != NULL); /* Get pointer to current or file scope entry. */ if (at_namespace_or_class_scope) { #if CHECKING if (var_ptr->storage_class != (a_storage_class)sc_static && var_ptr->storage_class != (a_storage_class)sc_extern && var_ptr->storage_class != (a_storage_class)sc_unspecified) { internal_error( "add_to_variables_list: bad storage class for file scope list"); } /* if */ #endif /* CHECKING */ } else if (ssep->kind == (a_scope_kind)sck_func_prototype) { /* This is an error case in which a variable is created in an old-style param declaration for which there was no corresponding param-id declaration. Such variables should not be added to the variables list anyway. */ sp = NULL; } else { /* Create the IL scope if necessary (for block scopes). */ sp = ensure_il_scope_exists(ssep); check_assertion_str(sp != NULL, "add_to_variables_list: NULL IL scope"); } /* if */ if (sp != NULL) { /* Variables requiring static allocation go on one list, those for stack and register allocation on another. */ if (at_namespace_or_class_scope || var_has_static_or_thread_storage_duration(var_ptr)) { /* Variables with static or thread storage duration will always be allocated in file scope memory region, regardless of which scope's list they are on. */ check_assertion_str( in_file_scope(var_ptr), "add_to_variables_list: var not in file scope region"); if (sp->variables == NULL) { sp->variables = var_ptr; } else if (pointers_block != NULL) { pointers_block->last_variable->next = var_ptr; } else { /* The scope stack entry is no longer on the stack, so just look for the end of the variables list and add the new variable. */ a_variable_ptr vp = sp->variables; while (vp->next != NULL) vp = vp->next; vp->next = var_ptr; } /* if */ if (pointers_block != NULL) pointers_block->last_variable = var_ptr; /* Record the parent scope for the variable. If a parent scope was already recorded, do not perform the update (e.g., when lowering moves static data members to file scope, the parent scope should remain the original class scope). */ if (!has_parent_scope(var_ptr)) { set_parent_scope(&var_ptr->source_corresp, iek_variable, sp); } /* if */ } else { check_assertion(ssep != NULL); /* Variables with nonstatic storage will be allocated in the file scope memory region only when the scope is a function prototype scope (i.e., in an error case) or in some cases involving GNU statement expressions. */ check_assertion_str(ssep->kind == sck_func_prototype || is_local_scope_kind(ssep->kind) || !in_file_scope(var_ptr), "add_to_variables_list: var in file scope region"); if (ssep->last_nonstatic_variable == NULL) { if (sp->nonstatic_variables != NULL) { /* We can only get here when expanding a structured binding pack for a for-range-declaration. */ a_variable_ptr vp = sp->nonstatic_variables; check_assertion(vp->is_struct_binding_container && vp->is_enhanced_for_iterator); while (vp->next != NULL) vp = vp->next; ssep->last_nonstatic_variable = vp; } /* if */ } /* if */ if (sp->nonstatic_variables == NULL) { sp->nonstatic_variables = var_ptr; } else { ssep->last_nonstatic_variable->next = var_ptr; } /* if */ ssep->last_nonstatic_variable = var_ptr; set_parent_scope(&var_ptr->source_corresp, iek_variable, sp); } /* if */ var_ptr->next = NULL; } /* if */ } /* add_to_variables_list */ void add_to_parameters_list(a_variable_ptr param_ptr) /* Add the given parameter to the parameters list for the current scope. */ { a_scope_stack_entry_ptr ssep; a_scope_ptr sp; /* Get pointer to current scope entry. */ ssep = &scope_stack[decl_scope_level]; sp = ssep->il_scope; #if CHECKING if (sp == NULL) internal_error("add_to_parameters_list: NULL IL scope"); #endif /* CHECKING */ if (sp->variant.routine.parameters == NULL) { sp->variant.routine.parameters = param_ptr; } else { ssep->last_parameter->next = param_ptr; } /* if */ ssep->last_parameter = param_ptr; param_ptr->next = NULL; set_parent_scope(¶m_ptr->source_corresp, iek_variable, sp); } /* add_to_parameters_list */ a_variable_ptr make_variable(a_type_ptr type_ptr, a_storage_class storage_class, a_scope_depth scope_depth) /* Allocate an entry for a variable with type type_ptr and storage class storage_class, and return a pointer to it. Unless scope_depth equals NO_SCOPE_DEPTH, add the variable to the indicated scope. */ { a_variable_ptr vp; /* Allocate the variable at the file scope if requested. Variables receiving static storage, even if they are not to go on the file scope variables list, should also be allocated in the file scope memory region. */ vp = alloc_variable(storage_class); vp->type = type_ptr; if (scope_depth != NO_SCOPE_DEPTH) { add_to_variables_list(vp, scope_depth); } /* if */ return vp; } /* make_variable */ a_variable_ptr make_handler_parameter(a_type_ptr type_ptr) /* Create a handler parameter variable with the specified type, add it to the parameter field of the current block scope, and return a pointer to it. */ { a_variable_ptr vp; db_enter(5, "make_handler_parameter"); /* Allocate the variable. */ vp = make_variable(type_ptr, (a_storage_class)sc_auto, decl_scope_level); vp->is_handler_param = TRUE; db_exit(); return vp; } /* make_handler_parameter */ void add_temporary_to_front_of_variables_list(a_variable_ptr temp, a_scope_ptr scope) /* Add the indicated temporary variable to the variables list of the indicated scope. Add it at the front of the list. temp may be a static or nonstatic variable. */ { a_scope_stack_entry_ptr ssep; a_variable_ptr *prev_ptr_ptr, *last_ptr_ptr; check_assertion(scope != NULL); set_parent_scope(&temp->source_corresp, iek_variable, scope); /* See if the scope we are adding to is active on the scope stack. If so, we have to maintain the "last" pointer too. */ ssep = NULL; if (scope->depth_in_scope_stack != NO_SCOPE_DEPTH) { ssep = &scope_stack[scope->depth_in_scope_stack]; } /* if */ /* The variable goes on either the static or the nonstatic variables list, so determine the proper pointers to adjust. */ last_ptr_ptr = NULL; if (temp->storage_class == (a_storage_class)sc_static) { prev_ptr_ptr = &scope->variables; if (ssep != NULL) { last_ptr_ptr = &(assoc_pointers_block_of(ssep)->last_variable); } /* if */ } else { prev_ptr_ptr = &scope->nonstatic_variables; if (ssep != NULL) last_ptr_ptr = &ssep->last_nonstatic_variable; } /* if */ /* The temporary goes at the front, but after any unnamed entities. That ensures that temporaries built later come after temporaries built earlier, which is needed when record_needed_destruction is called for a temporary. */ if (last_ptr_ptr != NULL && *last_ptr_ptr != NULL && !has_name(*last_ptr_ptr)) { /* Special case for a list that has only temporary variables. */ (*last_ptr_ptr)->next = temp; temp->next = NULL; *last_ptr_ptr = temp; } else { while (*prev_ptr_ptr != NULL && !has_name(*prev_ptr_ptr)) { prev_ptr_ptr = &(*prev_ptr_ptr)->next; } /* while */ temp->next = *prev_ptr_ptr; *prev_ptr_ptr = temp; if (last_ptr_ptr != NULL && temp->next == NULL) *last_ptr_ptr = temp; } /* if */ } /* add_temporary_to_front_of_variables_list */ a_variable_ptr alloc_temporary_variable(a_type_ptr temp_type, a_boolean force_static) /* Make a temporary variable whose type is temp_type. Return a pointer to it. If force_static is TRUE, make the temporary static (otherwise, it will be automatic if we're currently inside a function). This is used in some cases where the front end proper (as opposed to, say, IL lowering) needs to generate a helper variable. */ { a_variable_ptr temp_var; a_symbol_ptr sym; a_boolean at_file_scope; a_storage_class storage_class; a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; /* Typically, a temporary variable will have automatic storage class, since it will appear in an expression in a function or block scope. However, if the temp is involved in an expression at file scope (within a class scope, for instance, or a default argument expression), it should be static and be allocated in file scope memory region. */ if (curr_il_region_number == file_scope_region_number) { storage_class = (a_storage_class)sc_static; at_file_scope = TRUE; } else { /* We're inside a function, so the temporary can be auto, unless we're told to force a static temporary. */ at_file_scope = FALSE; if (force_static) { storage_class = (a_storage_class)sc_static; } else { storage_class = (a_storage_class)sc_auto; } /* if */ } /* if */ /* Allocate the variable. Do not put it on a scope variable list yet. make_variable/alloc_variable uses the appropriate memory region, based on storage class. */ temp_var = make_variable(temp_type, storage_class, NO_SCOPE_DEPTH); temp_var->compiler_generated = TRUE; /* Find the proper scope and add the temporary at the start of the scope's variable list. */ sp = get_scope_for_list(at_file_scope ? DEPTH_OF_FILE_SCOPE : decl_scope_level, &temp_var->source_corresp, &pointers_block); if (!at_file_scope) temp_var->source_corresp.is_local_to_function = TRUE; add_temporary_to_front_of_variables_list(temp_var, sp); /* Associate an unnamed symbol with the variable. */ sym = alloc_symbol((a_symbol_kind)sk_variable, (a_symbol_header_ptr)NULL, &error_position); sym->variant.variable.ptr = temp_var; temp_var->source_corresp.assoc_info = (char*)sym; /* Name linkage stays nlk_none. */ return temp_var; } /* alloc_temporary_variable */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_field_ptr next_applicable_field(a_field_ptr field, a_next_field_options_set options) /* Given a pointer to a field (or NULL), return a pointer to the first field at or after the given field that matches the criteria specified by "options". If there is no next such field, return NULL. */ { for (; field != NULL; field = field->next) { if (options & NF_SKIP_FIELDS_ADDED_BY_LOWERING && symbol_for(field) == NULL) { /* Skip fields generated by lowering. */ continue; } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (options & NF_SKIP_PROPERTY_OR_EVENT && field->property_or_event_descr != NULL) { /* Skip any property or event fields. */ continue; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (options & NF_SKIP_OPTIMIZED_EMPTY_CLASS && field->is_optimized_empty_class) { /* Skip fields for optimized empty classes. */ continue; } /* if */ if (options & NF_INITIALIZABLE) { /* Unnamed bit fields are not initializable. */ if (has_name(field) || !field->is_bit_field) break; /* Anonymous unions are also initializable in C++. An extension allows anonymous parent objects in C too. */ if (field->is_anonymous_parent_object) break; } else { /* Field has not been skipped and doesn't need to be initializable. */ break; } /* if */ } /* for */ return field; } /* next_applicable_field */ a_boolean is_compound_assignment_operator(an_expr_operator_kind op) /* Return TRUE if and only if the given operator is a compound assignment. */ { a_boolean result; switch (op) { case eok_add_assign: case eok_subtract_assign: case eok_multiply_assign: case eok_divide_assign: case eok_remainder_assign: case eok_shiftl_assign: case eok_shiftr_assign: case eok_and_assign: case eok_or_assign: case eok_xor_assign: case eok_padd_assign: case eok_psubtract_assign: result = TRUE; break; default: result = FALSE; break; } /* switch */ return result; } /* is_compound_assignment_operator */ #if !STANDALONE_UTILITY_PROGRAM #if FIXED_POINT_ALLOWED a_fixed_point_type_descr make_fixed_point_type_descr( a_fixed_point_precision precision, a_boolean is_unsigned, a_boolean is_fract, a_boolean saturating) /* Construct and return a fixed-point-type-descr with the given attributes. */ { a_fixed_point_type_descr descr; descr.precision = precision; descr.is_unsigned = is_unsigned; descr.is_fract_type = is_fract; descr.saturating = saturating; return descr; } /* make_fixed_point_type_descr */ a_type_ptr fixed_point_result_type(a_type_ptr type_1, a_type_ptr type_2) /* Determine the (fixed-point) result type for a binary expression on operands with the two given types. At least one of the types must be a fixed-point type, and the other is a fixed-point type, an integral type, or an enum type (i.e., a non-floating-point arithmetic type). */ { a_type_ptr result; a_type_ptr tp1 = skip_typerefs(type_1), tp2 = skip_typerefs(type_2); if (!is_fixed_point_type(tp1)) { /* The result type is the fixed-point type. */ result = type_2; } else if (!is_fixed_point_type(tp2)) { /* The result type is the fixed-point type. */ result = type_1; } else { /* Both types are fixed-point types. Determine the one with the highest rank. Also imbue any signedness and saturation on the result type. */ a_boolean saturating = (tp1->variant.fixed_point.saturating || tp2->variant.fixed_point.saturating); a_boolean is_unsigned = tp1->variant.fixed_point.is_unsigned && tp2->variant.fixed_point.is_unsigned; if (tp1->variant.fixed_point.is_fract_type != tp2->variant.fixed_point.is_fract_type) { /* _Accum types have higher rank than _Fract types. */ result = tp1->variant.fixed_point.is_fract_type ? tp2 : tp1; } else if (tp1->variant.fixed_point.precision > tp2->variant.fixed_point.precision) { /* If the fixed-point type kinds (_Fract vs. _Accum) are equal, the precisions determine the relative rank. */ result = tp1; } else { result = tp2; } /* if */ if (result->variant.fixed_point.saturating != saturating || (result->variant.fixed_point.is_unsigned && !is_unsigned)) { a_fixed_point_type_descr descr; descr = make_fixed_point_type_descr( result->variant.fixed_point.precision, is_unsigned, (a_boolean)result->variant. fixed_point.is_fract_type, saturating); /* We can't allocate a type in a back end, so this code doesn't work there without some special adjustment. */ check_assertion(in_front_end); result = fixed_point_type(descr); } /* if */ } /* if */ return result; } /* fixed_point_result_type */ #endif /* FIXED_POINT_ALLOWED */ a_type_ptr compound_assignment_operation_type(an_expr_node_ptr expr) /* Return the "operation type" of the given compound assignment node. Special care must be taken with fixed-point operands in particular. */ { a_type_ptr operation_type = expr->type, op1_type, op2_type; an_expr_operator_kind op; an_expr_node_ptr op1, op2; check_assertion(is_operation_node(expr)); op = expr->variant.operation.kind; check_assertion(is_compound_assignment_operator(op)); op1 = expr->variant.operation.operands; op2 = op1->next; op1 = skip_parens(op1); op2 = skip_parens(op2); op1_type = prvalue_type(op1->type); op2_type = op2->type; /* Usually, the operation type is the type of the second operand, because the second operand will have been cast to the operation type. */ operation_type = op2_type; if ((op == (an_expr_operator_kind)eok_shiftl_assign || op == (an_expr_operator_kind)eok_shiftr_assign) && C_dialect != C_dialect_pcc) { /* Shifts. The operation type is given by the first operand (after integral promotions). (pcc doesn't treat shifts differently from ordinary arithmetic operations.) */ operation_type = type_after_integral_promotion(op1_type); } else if (op == (an_expr_operator_kind)eok_padd_assign || op == (an_expr_operator_kind)eok_psubtract_assign) { /* Pointer += and -=. The operation type is given by the first operand. */ operation_type = op1_type; /* Except for the C++ bool += pointer case. */ if (is_bool_type(operation_type)) operation_type = op2_type; #if FIXED_POINT_ALLOWED } else if (is_fixed_point_type(op1_type) || is_fixed_point_type(op2_type)) { /* One or two fixed-point operands. The operation type must be computed from the operand types. This is more complicated than the usual case because the operands are not brought to a common type. */ /* Ensure that this is a C mode. Otherwise, we'd have to deal with template-dependent types. */ check_assertion(C_mode()); if (is_floating_type(op1_type)) { operation_type = op1_type; } else if (is_floating_type(op2_type)) { operation_type = op2_type; } else { operation_type = fixed_point_result_type(op1_type, op2_type); } /* if */ #endif /* FIXED_POINT_ALLOWED */ } /* if */ return operation_type; } /* compound_assignment_operation_type */ a_type_kind binary_operation_type_kind(an_expr_operator_kind op, a_type_ptr op1_type, a_type_ptr op2_type) /* Determine the "operation type kind" for the given binary operator kind applied to operands of the given type. */ { a_type_kind result = (a_type_kind)tk_unknown, kind1, kind2; op1_type = skip_typerefs(op1_type); op2_type = skip_typerefs(op2_type); kind1 = op1_type->kind; kind2 = op2_type->kind; if (op == (an_expr_operator_kind)eok_land || op == (an_expr_operator_kind)eok_lor) { /* Logical operators may have operands of any scalar types, but they are really an operation on two boolean/integer values. */ result = (a_type_kind)tk_integer; } else if (op == (an_expr_operator_kind)eok_comma) { result = (a_type_kind)tk_unknown; } else if (kind1 == kind2) { result = kind1; #if C99_IL_EXTENSIONS_SUPPORTED if (result == (a_type_kind)tk_imaginary) { /* "Imaginary op Imaginary" is usually equivalent to "float op float". The exception is "Imaginary * Imaginary", which requires an additional negation. We therefore use tk_imaginary only for the latter case. */ if (op != (an_expr_operator_kind)eok_multiply) { result = (a_type_kind)tk_float; } /* if */ } /* if */ #endif /* C99_IL_EXTENSIONS_SUPPORTED */ } else if (kind1 == (a_type_kind)tk_error || kind2 == (a_type_kind)tk_error) { result = (a_type_kind)tk_error; } else if (kind1 == (a_type_kind)tk_template_param || kind2 == (a_type_kind)tk_template_param || is_template_dependent_type(op1_type) || is_template_dependent_type(op2_type)) { result = (a_type_kind)tk_template_param; } else if (kind1 == (a_type_kind)tk_pointer || kind2 == (a_type_kind)tk_pointer) { result = (a_type_kind)tk_pointer; } else if (kind1 == (a_type_kind)tk_ptr_to_member || kind2 == (a_type_kind)tk_ptr_to_member) { result = (a_type_kind)tk_ptr_to_member; #if C99_IL_EXTENSIONS_SUPPORTED } else if (kind1 == (a_type_kind)tk_imaginary || kind2 == (a_type_kind)tk_imaginary) { /* Operations involving an imaginary operand and another type (tk_float or tk_complex). */ if (kind1 == (a_type_kind)tk_complex || kind2 == (a_type_kind)tk_complex) { result = (a_type_kind)tk_complex; } else { check_assertion(kind1 == (a_type_kind)tk_float || kind2 == (a_type_kind)tk_float); /* A mixed tk_float/tk_imaginary operation. Addition and subtraction operators don't actually require addition or subtraction, but the assembly of a complex value; so the operation type is recorded as tk_complex in that case. Division by an imaginary number requires a negation on top of the regular floating-point division; so that case has operation type tk_imaginary. Multiplication and other division cases only require the corresponding floating-point operation; we record tk_float for those. */ if (op == (an_expr_operator_kind)eok_fjadd || op == (an_expr_operator_kind)eok_jfadd || op == (an_expr_operator_kind)eok_fjsubtract || op == (an_expr_operator_kind)eok_jfsubtract) { result = (a_type_kind)tk_complex; } else if (kind2 == (a_type_kind)tk_imaginary && (op == (an_expr_operator_kind)eok_divide || op == (an_expr_operator_kind)eok_divide_assign)) { result = (a_type_kind)tk_imaginary; } else { result = (a_type_kind)tk_float; } /* if */ } /* if */ #endif /* C99_IL_EXTENSIONS_SUPPORTED */ #if FIXED_POINT_ALLOWED } else if (kind1 == (a_type_kind)tk_fixed_point) { result = (kind2 == (a_type_kind)tk_integer) ? kind1 : kind2; } else if (kind2 == (a_type_kind)tk_fixed_point) { result = (kind1 == (a_type_kind)tk_integer) ? kind2 : kind1; #endif /* FIXED_POINT_ALLOWED */ #if GNU_VECTOR_TYPES_ALLOWED } else if (kind1 == (a_type_kind)tk_vector || kind2 == (a_type_kind)tk_vector) { result = (a_type_kind)tk_vector; #endif /* GNU_VECTOR_TYPES_ALLOWED */ } else if (kind1 == (a_type_kind)tk_nullptr || kind2 == (a_type_kind)tk_nullptr) { result = (a_type_kind)tk_nullptr; } else if (is_template_dependent_context()) { result = tk_template_param; } else { unexpected_condition(); } /* if */ if (result == (a_type_kind)tk_class || result == (a_type_kind)tk_union) { /* Normalize the type kind to tk_struct for all class types. */ result = (a_type_kind)tk_struct; } /* if */ return result; } /* binary_operation_type_kind */ static a_type_kind operation_type_kind(an_expr_node_ptr expr) /* Return the kind of type the given enk_operation node acts on. This is best thought of as an indication of the kind of specialized machine register that might be used to implement the operation. E.g., when comparing two integers the result is tk_integer, when adding an integer to a pointer the result is tk_pointer, and when adding two vectors of integers the result is tk_vector. Many operations don't specifically apply to a particular type kind; for those tk_unknown is returned. */ { using an_underlying_type_kind = a_byte; /* If this assertion fails the underlying type of the a_type_kind enum has changed and the an_underlying_type_kind type needs to be updated. */ static_assert(sizeof(an_underlying_type_kind) == sizeof(a_type_kind), "operand_kind size does not match a_type_kind"); check_assertion(expr->kind == enk_operation); an_underlying_type_kind result = tk_unknown; an_expr_node_ptr op1 = expr->variant.operation.operands; a_type_kind expr_kind = skip_typerefs(expr->type)->kind; an_underlying_type_kind operand_kind; if (op1 == NULL) { /* In some rare cases, the operand is not attached yet when this routine is called. That should only happen when the operand type is not used in determining the operation type kind (checked below). */ #define INVALID_TYPE_KIND ((an_underlying_type_kind)~0) operand_kind = INVALID_TYPE_KIND; } else { operand_kind = skip_typerefs(op1->type)->kind; } /* if */ switch (expr->variant.operation.kind) { case eok_address_of: case eok_reference_to: case eok_handle_to: case eok_indirect: case eok_ref_indirect: case eok_box: case eok_handle_to_box: case eok_unbox: case eok_unbox_lvalue: result = (a_type_kind)tk_pointer; break; case eok_cast: case eok_lvalue_cast: case eok_ref_cast: case eok_lvalue_adjust: /* Since eok_cast and the similar operators potentially involve unrelated type kinds, we do not attempt to characterize an "operation type" for these cases if the source and destination type kind are different. */ if (expr_kind == operand_kind) { result = expr_kind; } else { result = (a_type_kind)tk_unknown; } /* if */ break; case eok_class_rvalue_adjust: result = (a_type_kind)tk_struct; break; case eok_base_class_cast: case eok_derived_class_cast: result = expr_kind; break; case eok_pm_base_class_cast: case eok_pm_derived_class_cast: result = operand_kind; break; case eok_dynamic_cast: result = (a_type_kind)tk_pointer; break; case eok_ref_dynamic_cast: result = expr_kind; break; case eok_bool_cast: result = operand_kind; break; case eok_array_to_pointer: result = (a_type_kind)tk_array; break; #if MICROSOFT_EXTENSIONS_ALLOWED case eok_assume: result = (a_type_kind)tk_integer; break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case eok_noexcept: result = (a_type_kind)tk_integer; break; case eok_parens: case eok_negate: case eok_unary_plus: case eok_complement: result = expr_kind; break; case eok_not: result = operand_kind; break; #if GNU_VECTOR_TYPES_ALLOWED case eok_vector_not: case eok_vector_fill: result = (a_type_kind)tk_vector; break; #endif /* GNU_VECTOR_TYPES_ALLOWED */ #if C99_IL_EXTENSIONS_SUPPORTED case eok_xconj: case eok_real_part: case eok_imag_part: #endif /* C99_IL_EXTENSIONS_SUPPORTED */ case eok_post_incr: case eok_post_decr: case eok_pre_incr: case eok_pre_decr: result = expr_kind; break; case eok_add: case eok_subtract: case eok_multiply: case eok_divide: case eok_remainder: result = expr_kind; break; #if C99_IL_EXTENSIONS_SUPPORTED case eok_jmultiply: case eok_jdivide: result = (a_type_kind)tk_imaginary; break; case eok_fjadd: case eok_jfadd: case eok_fjsubtract: case eok_jfsubtract: result = (a_type_kind)tk_complex; break; #endif /* C99_IL_EXTENSIONS_SUPPORTED */ case eok_padd: case eok_psubtract: case eok_pdiff: result = (a_type_kind)tk_pointer; break; case eok_shiftl: case eok_shiftr: /* The "operation type kind" is determined by the first operand (the value being shifted). */ result = operand_kind; break; case eok_and: case eok_or: case eok_xor: result = expr_kind; break; case eok_eq: case eok_ne: case eok_gt: case eok_lt: case eok_ge: case eok_le: case eok_spaceship: if (expr_kind == (a_type_kind)tk_template_param) { /* If either operand has a tk_template_param type, the call to binary_operation_type_kind below will produce a tk_template_param kind also. However, in some modes, an expression in a prototype instantiation may be considered template-dependent even when its arguments are not. */ result = expr_kind; } else { an_expr_node_ptr arg1 = expr->variant.operation.operands, arg2 = arg1->next; result = binary_operation_type_kind(expr->variant.operation.kind, arg1->type, arg2->type); } /* if */ break; #if GNU_VECTOR_TYPES_ALLOWED case eok_vector_eq: case eok_vector_ne: case eok_vector_gt: case eok_vector_lt: case eok_vector_ge: case eok_vector_le: result = (a_type_kind)tk_vector; break; #endif /* GNU_VECTOR_TYPES_ALLOWED */ case eok_gnu_min: case eok_gnu_max: result = expr_kind; break; case eok_assign: result = operand_kind; break; case eok_add_assign: case eok_subtract_assign: case eok_multiply_assign: case eok_divide_assign: case eok_remainder_assign: case eok_shiftl_assign: case eok_shiftr_assign: case eok_and_assign: case eok_or_assign: case eok_xor_assign: { a_type_ptr op_type = compound_assignment_operation_type(expr); result = skip_typerefs(op_type)->kind; } break; case eok_padd_assign: case eok_psubtract_assign: result = (a_type_kind)tk_pointer; break; case eok_bassign: result = (a_type_kind)tk_unknown; break; case eok_land: case eok_lor: result = (a_type_kind)tk_integer; break; #if GNU_VECTOR_TYPES_ALLOWED case eok_vector_land: case eok_vector_lor: result = (a_type_kind)tk_vector; break; #endif /* GNU_VECTOR_TYPES_ALLOWED */ case eok_comma: result = (a_type_kind)tk_unknown; break; case eok_subscript: result = (a_type_kind)tk_pointer; break; #if GNU_VECTOR_TYPES_ALLOWED case eok_vector_subscript: result = (a_type_kind)tk_vector; break; #endif /* GNU_VECTOR_TYPES_ALLOWED */ case eok_cli_subscript: case eok_dot_field: case eok_points_to_field: case eok_pm_field: case eok_pm_points_to_field: case eok_dot_pm_func_ptr: case eok_points_to_pm_func_ptr: case eok_dot_static: case eok_points_to_static: case eok_virtual_function_ptr: case eok_dot_vacuous_destructor_call: case eok_points_to_vacuous_destructor_call: case eok_question: case eok_call: case eok_dot_member_call: case eok_points_to_member_call: case eok_dot_pm_call: case eok_points_to_pm_call: case eok_va_start: case eok_va_arg: case eok_va_end: case eok_va_copy: case eok_va_start_single_operand: result = (a_type_kind)tk_unknown; break; #if GNU_VECTOR_TYPES_ALLOWED case eok_vector_question: result = (a_type_kind)tk_vector; break; #endif /* GNU_VECTOR_TYPES_ALLOWED */ case eok_lvalue: result = expr_kind; break; case eok_await: case eok_yield: result = expr_kind; break; case eok_splice: result = tk_reflection; break; case eok_error: result = (a_type_kind)tk_error; break; default: unexpected_condition_str("operation_type_kind: unexpected operation"); } /* switch */ if (result == (a_type_kind)tk_class || result == (a_type_kind)tk_union) { /* Normalize the type kind to tk_struct for all class types. */ result = (a_type_kind)tk_struct; } /* if */ /* See above for special handling when the operation has no operand. Verify that we didn't draw any conclusion from the operand type. */ check_assertion(result != INVALID_TYPE_KIND); return (a_type_kind)result; } /* operation_type_kind */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_constant_ptr constant_value_of_dynamic_init(a_dynamic_init_ptr dip) /* Return the constant value of a dynamic initialization, if it has one, otherwise NULL. */ { a_constant_ptr con = NULL; if (dip->kind == (a_dynamic_init_kind)dik_constant) { con = dip->variant.constant.ptr; } else if (dip->kind == (a_dynamic_init_kind)dik_expression) { an_expr_node_ptr expr = dip->variant.expression; if (expr != NULL && is_constant_node(expr)) { con = node_constant(expr); } /* if */ } /* if */ return con; } /* constant_value_of_dynamic_init */ a_dynamic_init_ptr skip_constexpr_init_folding(a_dynamic_init_ptr dip) /* If dip is an initialization to a constant value obtained by folding another dynamic initialization entry, return the dynamic init entry that was folded; otherwise, return dip. (The original entry will be pointed to by an enk_temp_init or enk_initializer entry in the backing expression of the a_constant entry produced by folding.) */ { a_constant_ptr con = constant_value_of_dynamic_init(dip); if (con != NULL) { /* The original form of the constructor call is stored as a backing expression under the constant. */ an_expr_node_ptr expr = con->expr; if (expr != NULL) { if (expr->kind == (an_expr_node_kind)enk_temp_init && con->is_result_of_constexpr_call) { dip = expr->variant.init.dynamic_init; } else if (expr->kind == (an_expr_node_kind)enk_initializer) { dip = expr->variant.initializer.dyn_init; } /* if */ } /* if */ } /* if */ return dip; } /* skip_constexpr_init_folding */ a_dynamic_init_ptr effective_dynamic_init_for_initializer_list_object( a_dynamic_init_ptr dip, a_type_ptr *init_entity_type) /* dip is a dynamic initialization for the creation of a std::initializer_list object from a braced initializer. The dynamic initialization entry calls a hidden constructor passing a temporary array initialized by the contents of the braced-init-list. Return the dynamic initialization entry for the underlying temporary array, and the type for that in *init_entity_type, thus allowing the caller to skip over the implicit part of that operation. init_entity_type can be NULL if the caller does not need that information. */ { an_expr_node_ptr arg1; check_assertion(dip->is_creation_of_initializer_list_object); dip = skip_constexpr_init_folding(dip); check_assertion(dip->kind == (a_dynamic_init_kind)dik_constructor); arg1 = dip->variant.constructor.args; check_assertion(arg1 != NULL); /* Skip compiler-generated casts as well as any enk_temp_init nodes introduced to reuse the address of the initialized temporary array. */ for (;;) { if (is_cast_operation_node(arg1) && arg1->compiler_generated) { arg1 = arg1->variant.operation.operands; } else if (arg1->kind == (an_expr_node_kind)enk_temp_init && arg1->variant.init.dynamic_init->is_reused_value) { check_assertion(arg1->variant.init.dynamic_init->kind == (a_dynamic_init_kind)dik_expression); arg1 = arg1->variant.init.dynamic_init->variant.expression; } else { break; } /* if */ } /* for */ check_assertion(is_operation_node(arg1) && node_operator_is(arg1, eok_array_to_pointer)); arg1 = arg1->variant.operation.operands; check_assertion(arg1->kind == (an_expr_node_kind)enk_temp_init); dip = arg1->variant.init.dynamic_init; if (init_entity_type != NULL) *init_entity_type = arg1->type; return dip; } /* effective_dynamic_init_for_initializer_list_object */ #if !STANDALONE_UTILITY_PROGRAM /* The routines lists in file and namespace scopes is normally constructed by appending newly created routine entries at the end of the appropriate list. The one exception occurs when a previously declared routine is defined: In that case we must move the original entry to the end of the list. Since the routines list is singly-linked, traversing the list each time we encounter a definition can be prohibitively expensive with the following not-so-rare scenario. Suppose the sequence of routine declarations in a translation unit is as follows: ... N large ... ... (The large number of initial routine declarations is a common consequence of including library header files.) Each definition of F, G, ... requires that the entry corresponding to the prior declaration of F, G, ... be found, which in turn requires a traversal of a large portion of the list of routines. If we find and move prior declarations one at a time, we end up performing an expensive traversal for every definition, and the total cost of these traversals can become a very significant fraction of total compilation time. Instead, we attempt to amortize the cost of traversing the routines list by scheduling moves in an array of "move descriptions". At an appropriate time (e.g., when a scope is popped), these "moves" are then sorted to enhance the likeliness that a simultaneous traversal of the routines list and the array of move descriptions will allow most or all scheduled moves to be performed. */ typedef struct a_routine_move_descr { a_routine_ptr routine; /* A pointer to a routine that must be moved. */ a_routine_ptr insert_after; /* A pointer to a placeholder routine entry after which the routine should be inserted. (Placeholder routines are never themselves moved.) */ a_seq_number first_pos; /* The sequence number of the first declaration of the routine to move. This position participates in the sorting of the "move descriptions" and must be recorded when the move is scheduled because the value in the routine will be modified to reflect the position of the definition. */ } a_routine_move_descr; STATIC_THREAD a_routine_move_descr *scheduled_routine_moves; /* A pointer to an array of routine move descriptions. */ STATIC_THREAD a_scope_ptr scope_of_scheduled_routine_moves; /* A pointer to the scope of the routines currently scheduled to be moved. If a routine in another scope needs to be moved, the previously scheduled moves are performed and this pointer is updated to point to the scope of the newly scheduled routine. */ STATIC_THREAD a_scope_pointers_block_ptr scope_pointers_of_scheduled_routine_moves; /* A pointer to the "pointers block" associated with by scope_of_scheduled_routine_moves. */ STATIC_THREAD a_routine_ptr *routine_move_placeholders; /* A pointer to an array of pointers to placeholder routines. */ STATIC_THREAD sizeof_t n_scheduled_routine_moves; #define MAX_N_SCHEDULED_ROUTINE_MOVES 100 #if DEBUG void db_scheduled_routine_moves(void) /* Display the current list of moves to perform. */ { sizeof_t k; for (k = 0; k < n_scheduled_routine_moves; ++k) { a_routine_ptr rp = scheduled_routine_moves[k].routine, after = scheduled_routine_moves[k].insert_after; fprintf(f_debug, "[%3lu] \"%s\" goes after \"%s\"\n", (unsigned long)k, rp == NULL ? "" : rp->source_corresp.name, after == NULL ? "" : after->source_corresp.name); } /* for */ } /* db_scheduled_routine_moves */ #endif /* DEBUG */ static a_boolean compare_routine_move(a_routine_move_descr const &x, a_routine_move_descr const &y) /* Return TRUE if the routine move described by x should be ordered before that described by y. The entry with the later first_pos is ordered first so that routines originally declared earlier are moved first when the array is traversed from the end. Since multiple routine declarations are rarely placed on the same line, the column ordering is ignored. */ { return y.first_pos < x.first_pos; } /* compare_routine_move */ void perform_scheduled_routine_moves(void) /* Perform routine moves described by the array scheduled_routine_moves. This function attempts to amortize the cost of traversing the routines list of a scope by taking advantage of the fact that routines generally appear in declaration order on their list. */ { a_routine_move_descr *rmdp; a_routine_ptr routine, *p_rp; sizeof_t n; if (n_scheduled_routine_moves == 0) { /* There are no moves to be performed. */ goto done; } /* if */ /* Sort the moves in the order of the original declarations of the routines (which is normally the order in which the routines appear on their respective scope lists). (We actually invert that order.) */ sort(scheduled_routine_moves, scheduled_routine_moves + n_scheduled_routine_moves, compare_routine_move); /* Traverse the ordered move schedule and find each routine-to-move on its scope list. */ rmdp = &scheduled_routine_moves[n_scheduled_routine_moves-1]; routine = rmdp->routine; n = n_scheduled_routine_moves; a_boolean any_moves; do { /* In most cases, all scheduled routine moves will be handled through a single traversal of the routines list (which is achieved by the inner do { ... } while loop). However, a few situations may cause the ordering by sequence number not to parallel the ordering on the routines list (e.g., when multiple routines are declared on a single line). We therefore repeat the traversal until all scheduled moves have been performed. */ any_moves = FALSE; p_rp = &scope_of_scheduled_routine_moves->routines; do { if (routine == *p_rp) { /* We found the routine: Remove it from the list and insert it after its placeholder. (We'll remove the placeholders in a separate pass.) */ *p_rp = routine->next; routine->next = rmdp->insert_after->next; rmdp->insert_after->next = routine; if (routine->next == NULL) { /* routine moved to the last position in the list. */ scope_pointers_of_scheduled_routine_moves->last_routine = routine; } /* if */ if (--n == 0) { goto remove_placeholders; } /* if */ --rmdp; routine = rmdp->routine; any_moves = TRUE; } else { p_rp = &(*p_rp)->next; } /* if */ } while (*p_rp != NULL); } while (any_moves); /* If this assertion fails, at least one routine that was scheduled to be moved could not be found. */ check_assertion(n == 0); remove_placeholders: /* Traverse the routines list once more to remove the placeholder routines (which are identified by their unique "source_corresp.name" pointer). */ p_rp = &scope_of_scheduled_routine_moves->routines; while (n_scheduled_routine_moves > 0) { check_assertion(*p_rp != NULL); if ((*p_rp)->source_corresp.name == routine_move_placeholder_name) { *p_rp = (*p_rp)->next; --n_scheduled_routine_moves; } else { p_rp = &(*p_rp)->next; } /* if */ } /* while */ scope_of_scheduled_routine_moves = NULL; scope_pointers_of_scheduled_routine_moves = NULL; done:; } /* perform_scheduled_routine_moves */ void schedule_move_to_current_end_of_routines_list(a_routine_ptr rp) /* Record the given routine as needing to be moved to what is currently the end of the routines list for that scope. In most cases, the actual move will be performed later by a call to perform_scheduled_routine_moves (to amortize the cost of list traversals over multiple moves). */ { a_routine_move_descr *rmdp; a_routine_ptr placeholder; a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; /* Get the pointers block for the scope in which the routine is declared. */ sp = get_scope_for_list(NO_SCOPE_DEPTH, &rp->source_corresp, &pointers_block); check_assertion(sp != NULL && pointers_block != NULL && pointers_block->last_routine != NULL); if (rp == pointers_block->last_routine) { /* The routine is already in place: Nothing to do. */ goto done; } /* if */ if (scheduled_routine_moves == NULL) { /* This is the first routine that needs to be moved: Allocate the helper data structures. */ scheduled_routine_moves = (a_routine_move_descr*) alloc_fe(MAX_N_SCHEDULED_ROUTINE_MOVES * sizeof(a_routine_move_descr)); routine_move_placeholders = (a_routine_ptr*) alloc_fe(MAX_N_SCHEDULED_ROUTINE_MOVES * sizeof(a_routine_ptr)); memzero((char *)routine_move_placeholders, MAX_N_SCHEDULED_ROUTINE_MOVES * sizeof(a_routine_ptr)); } else if (n_scheduled_routine_moves == MAX_N_SCHEDULED_ROUTINE_MOVES) { /* The routine schedule is full: Process it to make space for the new move to be scheduled. */ perform_scheduled_routine_moves(); } /* if */ if (scope_of_scheduled_routine_moves != sp) { if (scope_of_scheduled_routine_moves != NULL) { /* This move is for a scope that is different from the scope for the moves already in the schedule. Process the schedule first, then start a new schedule for the new scope. */ perform_scheduled_routine_moves(); } /* if */ scope_of_scheduled_routine_moves = sp; scope_pointers_of_scheduled_routine_moves = pointers_block; } /* if */ check_assertion_str(!rp->source_corresp.is_class_member, "class member not expected"); /* Insert a placeholder at the end of the routines list. This is useful to record a fixed insertion point for the scheduled moves (other routine entries may themselves be scheduled to move). */ placeholder = routine_move_placeholders[n_scheduled_routine_moves]; if (placeholder == NULL) { placeholder = routine_move_placeholders[n_scheduled_routine_moves] = alloc_routine(); placeholder->source_corresp.name = routine_move_placeholder_name; } /* if */ placeholder->next = NULL; placeholder->type = rp->type; pointers_block->last_routine->next = placeholder; pointers_block->last_routine = placeholder; rmdp = &scheduled_routine_moves[n_scheduled_routine_moves]; rmdp->routine = rp; rmdp->insert_after = placeholder; rmdp->first_pos = rp->source_corresp.decl_position.seq; ++n_scheduled_routine_moves; done:; } /* schedule_move_to_current_end_of_routines_list */ void remove_from_routines_list(a_routine_ptr rout_ptr, a_scope_depth scope_depth) /* Unlink the given routine from the routines list for the scope indicated by scope_depth. This is done so the routine can be added again at the end of the list, to keep the routines in order of appearance of their definitions. When scope_level is NO_SCOPE_DEPTH, the scope is computed rather than determined directly. */ { a_routine_ptr prev_routine = NULL, rp; a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; check_assertion_str(!rout_ptr->source_corresp.is_class_member, "remove_from_routines_list: class member not expected"); /* Get a pointer to the scope in which the routine is declared. */ sp = get_scope_for_list(scope_depth, &rout_ptr->source_corresp, &pointers_block); check_assertion_str(sp != NULL, "remove_from_routines_list: NULL IL scope"); /* Find the routine on the current list in order to find the previous entry so we can unlink. */ for (rp = sp->routines; rp != NULL; prev_routine = rp, rp = rp->next) { if (rp == rout_ptr) break; } /* for */ check_assertion_str(rp != NULL, "remove_from_routines_list: routine not found on list"); /* Link the previous entry to the entry following this one. */ if (prev_routine == NULL) { sp->routines = rout_ptr->next; } else { prev_routine->next = rout_ptr->next; } /* if */ /* If the entry being removed was the last on the list, update the last_routine pointer. */ if (pointers_block != NULL && rout_ptr == pointers_block->last_routine) { pointers_block->last_routine = prev_routine; } /* if */ } /* remove_from_routines_list */ void add_to_routines_list(a_routine_ptr rout_ptr, a_scope_depth scope_level) /* Add the given routine to the routines list for the scope corresponding to scope_level. When scope_level is NO_SCOPE_DEPTH, the scope is computed rather than determined directly. */ { a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; sp = get_scope_for_list(scope_level, &rout_ptr->source_corresp, &pointers_block); check_assertion_str(sp != NULL, "add_to_routines_list: NULL IL scope"); if (parent_scope_of(rout_ptr) == NULL) { /* Only set the parent scope the first time the routine is added to a routines list. It may be moved from that list to another list later on, but conceptually it is still a member of the original scope it appeared in (for example, lowering moves member functions to file scope, but we want to preserve the original scope). */ set_parent_scope(&rout_ptr->source_corresp, iek_routine, sp); } /* if */ /* Add the routine to the list of routines for this scope. */ if (sp->routines == NULL) { sp->routines = rout_ptr; } else if (pointers_block != NULL) { pointers_block->last_routine->next = rout_ptr; } else { /* The scope stack entry is no longer on the stack, so just look for the end of the routines list and add the new routine. */ a_routine_ptr rp = sp->routines; while (rp->next != NULL) rp = rp->next; rp->next = rout_ptr; } /* if */ rout_ptr->next = NULL; if (pointers_block != NULL) pointers_block->last_routine = rout_ptr; } /* add_to_routines_list */ void add_to_asm_entries_list(an_asm_entry_ptr asm_entry_ptr) /* Add the given routine to the asm entries list for the current scope. */ { a_scope_stack_entry_ptr ssep; a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; ssep = &scope_stack[decl_scope_level]; /* Create the IL scope if necessary (for block scopes). */ sp = ensure_il_scope_exists(ssep); check_assertion_str(sp != NULL, "add_to_asm_entries_list: NULL IL scope"); pointers_block = assoc_pointers_block_of(ssep); if (sp->asm_entries == NULL) { sp->asm_entries = asm_entry_ptr; } else { pointers_block->last_asm_entry->next = asm_entry_ptr; } /* if */ pointers_block->last_asm_entry = asm_entry_ptr; asm_entry_ptr->next = NULL; if (parent_scope_of(asm_entry_ptr) == NULL) { set_parent_scope(&asm_entry_ptr->source_corresp, iek_asm_entry, sp); } /* if */ } /* add_to_asm_entries_list */ void add_to_labels_list(a_label_ptr label_ptr) /* Add the given label to the labels list of the appropriate scope. Usually, that is the current function scope, but for locally declared labels it might be the current block scope. */ { /* Get pointer to the current function scope entry. */ #if GNU_EXTENSIONS_ALLOWED a_scope_ptr scope = label_ptr->locally_declared ? scope_stack[decl_scope_level].il_scope : innermost_function_scope; #else /* !GNU_EXTENSIONS_ALLOWED */ a_scope_ptr scope = innermost_function_scope; #endif /* GNU_EXTENSIONS_ALLOWED */ check_assertion_str(innermost_function_scope != NULL, "add_to_labels_list: not inside function"); if (scope->depth_in_scope_stack != NO_SCOPE_DEPTH) { /* The function scope is on the scope stack. */ a_scope_stack_entry_ptr ssep = &scope_stack[scope->depth_in_scope_stack]; /* Add the label at the end of the list. */ if (scope->labels == NULL) { scope->labels = label_ptr; } else { ssep->last_label->next = label_ptr; } /* if */ ssep->last_label = label_ptr; label_ptr->next = NULL; } else { /* The function scope is not on the scope stack, as in generated routines in IL lowering. */ /* Add the label at the beginning of the list. */ label_ptr->next = scope->labels; scope->labels = label_ptr; } /* if */ set_parent_scope(&label_ptr->source_corresp, iek_label, scope); } /* add_to_labels_list */ void set_expr_result_not_used(an_expr_node_ptr node) /* Mark the given node to indicate that its result is not used, i.e., that the value of the expression is discarded. */ { node->result_is_not_used = TRUE; /* For some operations, subnodes get marked too. This is only done with operations that have void type, because otherwise we run into problems if we rewrite the subnodes thinking they're top-level nodes -- it changes the subnode type without changing the parent node type (or the sibling node type, in the "?" case). The transformation done in lower_temp_init is the difficult case. */ /* If you change this, see also check_operation_node_consistency and check_result_not_used_flag. */ if (node->kind == (an_expr_node_kind)enk_operation && is_void_type(node->type)) { an_expr_operator_kind op = node->variant.operation.kind; an_expr_node_ptr operand_1 = node->variant.operation.operands; if (op == (an_expr_operator_kind)eok_comma) { /* Given a comma operation, the second operand is not used if the entire operation is not used. */ set_expr_result_not_used(operand_1->next); } else if (op == (an_expr_operator_kind)eok_question || op == (an_expr_operator_kind)eok_vector_question) { /* Given a question mark operation, the second and third operands are not used if the entire operation is not used. */ set_expr_result_not_used(operand_1->next); set_expr_result_not_used(operand_1->next->next); } else if (op == (an_expr_operator_kind)eok_cast) { /* The operand of a void cast is not used if the entire operation is not used. */ set_expr_result_not_used(operand_1); } else if (op == (an_expr_operator_kind)eok_parens) { /* Given parentheses, the operand is not used if the entire operation is not used. */ set_expr_result_not_used(operand_1); } /* if */ } else if (node->kind == (an_expr_node_kind)enk_object_lifetime) { set_expr_result_not_used(node->variant.object_lifetime.expr); } /* if */ } /* set_expr_result_not_used */ void set_node_operator(an_expr_node_ptr node, an_expr_operator_kind kind, a_type_ptr type, a_boolean is_lvalue, an_expr_node_ptr operands) /* Set the operator, type, and operand list in an operator expression node. Set the node's lvalueness as specified by is_lvalue. Also set the "operation type kind" as a function of the operator kind and the type of the operands. */ { node->type = type; node->orig_lvalue_type = NULL; node->variant.operation.kind = kind; node->variant.operation.operands = operands; node->is_lvalue = is_lvalue; node->is_xvalue = FALSE; #if DO_IL_LOWERING if (il_lowering_underway && strict_cpp17_eval_order) { /* Lowering is introducing a new operation; make sure it uses the canonical setting for the evaluation order for the specified operation. */ a_boolean eval_left_to_right, eval_right_to_left; if (kind == (an_expr_operator_kind)eok_call) { /* Note that lowering should only create eok_call nodes (and not other types of call nodes), so they are not checked here. */ if (operands != NULL && is_routine_node(operands) && special_kind_is(node_routine(operands), sfk_operator)) { eval_order_for_op_kind(node_routine(operands)->variant.opname_kind, &eval_left_to_right, &eval_right_to_left); node->variant.operation.eval_left_to_right = eval_left_to_right; node->variant.operation.eval_right_to_left = eval_right_to_left; } else { node->variant.operation.eval_left_to_right = TRUE; } /* if */ } else { eval_order_for_binary_node_kind(kind, &eval_left_to_right, &eval_right_to_left); node->variant.operation.eval_left_to_right = eval_left_to_right; node->variant.operation.eval_right_to_left = eval_right_to_left; } /* if */ } /* if */ if (il_lowering_underway && is_lvalue && (kind == (an_expr_operator_kind)eok_comma || kind == (an_expr_operator_kind)eok_assign || kind == (an_expr_operator_kind)eok_question || kind == (an_expr_operator_kind)eok_vector_question)) { /* Make sure that operators added during lowering have the returns_lvalue_instead_of_usual_rvalue field set properly. */ /* Note that these lvalue returning operations will be rewritten in optimize_lowered_expression_if_possible to produce rvalue operations (since these are not valid C constructs). */ node->variant.operation.returns_lvalue_instead_of_usual_rvalue = TRUE; } else #endif /* DO_IL_LOWERING */ /* Do not insert code here. */ { node->variant.operation.returns_lvalue_instead_of_usual_rvalue = FALSE; } /* if */ if (kind == (an_expr_operator_kind)eok_comma) { /* The value of the first operand of a comma operator is not used. */ set_expr_result_not_used(operands); } /* if */ /* The result_is_not_used flag of the node is preserved (it depends on what's pointing to this node rather than the node itself). However, the subnodes (newly attached) may need to be flagged. This comes up when existing expression trees are being changed, as in IL lowering. */ if (node->result_is_not_used) { set_expr_result_not_used(node); } /* if */ node->variant.operation.type_kind = operation_type_kind(node); } /* set_node_operator */ void copy_node_value_category(an_expr_node_ptr from, an_expr_node_ptr to) /* Copy the value category markers from the expression "from" to the expression "to". */ { to->is_lvalue = from->is_lvalue; to->is_xvalue = from->is_xvalue; } /* copy_node_value_category */ an_expr_node_ptr make_operator_node(an_expr_operator_kind kind, a_type_ptr type, an_expr_node_ptr operands) /* Allocate and initialize an operator node, give it a type and kind, and link an operands list to it. The node is created as an rvalue. */ { an_expr_node_ptr node; node = alloc_expr_node((an_expr_node_kind)enk_operation); set_node_operator(node, kind, type, /*is_lvalue=*/FALSE, operands); return node; } /* make_operator_node */ an_expr_node_ptr make_lvalue_operator_node(an_expr_operator_kind kind, a_type_ptr type, an_expr_node_ptr operands) /* Allocate and initialize an operator node, give it a type and kind, and link an operands list to it. The node is created as an lvalue. */ { an_expr_node_ptr node; node = make_operator_node(kind, type, operands); node->is_lvalue = TRUE; return node; } /* make_lvalue_operator_node */ an_expr_node_ptr make_comma_node(an_expr_node_ptr expr1, an_expr_node_ptr expr2) /* Make a comma expression node with the indicated two expressions as its operands, and return a pointer to it. The value category of the returned expression is determined by the value category of its second operand. */ { an_expr_node_ptr comma_node; expr1->next = expr2; expr2->next = NULL; comma_node = make_operator_node((an_expr_operator_kind)eok_comma, expr2->type, expr1); /* The value category of the comma operation is determined by its second operand. */ copy_node_value_category(expr2, comma_node); if (is_glvalue_node(comma_node)) { comma_node->variant.operation.returns_lvalue_instead_of_usual_rvalue= TRUE; } /* if */ return comma_node; } /* make_comma_node */ an_expr_node_ptr make_comma_node_if_necessary(an_expr_node_ptr node1, an_expr_node_ptr node2) /* Utility to combine two non-NULL expressions into a comma expression or return the "other" expression if one is NULL. Both expressions cannot be NULL. */ { an_expr_node_ptr result; check_assertion(node1 != NULL || node2 != NULL); if (node1 == NULL) { result = node2; } else if (node2 == NULL) { result = node1; } else { result = make_comma_node(node1, node2); } /* if */ return result; } /* make_comma_node_if_necessary */ an_expr_node_ptr error_node(void) /* Make and return an expression node indicating an error. */ { an_expr_node_ptr node; node = alloc_expr_node((an_expr_node_kind)enk_error); node->type = error_type(); return node; } /* error_node */ an_expr_node_ptr fs_error_node(void) /* Make and return an expression node indicating an error. Allocate it in the file scope memory region. */ { an_expr_node_ptr node; a_memory_region_number region_to_switch_back_to = NULL_region_number; switch_to_file_scope_region(®ion_to_switch_back_to); node = error_node(); switch_back_to_original_region(region_to_switch_back_to); return node; } /* fs_error_node */ void overwrite_node(an_expr_node_ptr node, an_expr_node_ptr source_node) /* Overwrite the expression node "node" with the contents of the node "source_node". This is used to remove do-nothing nodes by promoting their operands. The routine does nothing if the source and destination nodes are the same. */ { an_expr_node_ptr node_next; a_boolean result_is_not_used; #if DO_IL_LOWERING a_boolean is_non_normalized_boolean_controlling_expr; #endif /* DO_IL_LOWERING */ if (node != source_node) { /* Copy the node. Preserve the original "next" field, result_is_not_used, and is_non_normalized_boolean_controlling_expr flags. */ node_next = node->next; result_is_not_used = node->result_is_not_used; #if DO_IL_LOWERING is_non_normalized_boolean_controlling_expr = node->is_non_normalized_boolean_controlling_expr; #endif /* DO_IL_LOWERING */ /* Note that the new/delete supplement from the source node is used by the destination node; no copy is needed. */ *node = *source_node; node->next = node_next; node->result_is_not_used = result_is_not_used; #if DO_IL_LOWERING node->is_non_normalized_boolean_controlling_expr = is_non_normalized_boolean_controlling_expr; #endif /* DO_IL_LOWERING */ if (result_is_not_used) set_expr_result_not_used(node); } /* if */ } /* overwrite_node */ an_expr_node_ptr alloc_node_for_constant(a_constant *constant) /* Allocate an expression node for a constant. The constant provided need not already be allocated in the IL -- it will be allocated as a shareable constant. */ { an_expr_node_ptr node; node = alloc_expr_node((an_expr_node_kind)enk_constant); node_constant(node) = alloc_shareable_constant(constant); node->type = constant->type; node->extra.rescan_info = constant->rescan_info; return node; } /* alloc_node_for_constant */ an_expr_node_ptr alloc_node_for_allocated_constant(a_constant *constant) /* Allocate an expression node for a constant, and return a pointer to the expression node. The constant in this case is an already-allocated IL constant. */ { an_expr_node_ptr expr; expr = alloc_expr_node((an_expr_node_kind)enk_constant); node_constant(expr) = constant; expr->type = constant->type; return expr; } /* alloc_node_for_allocated_constant */ an_expr_node_ptr node_for_integer_constant(long value, an_integer_kind kind) /* Make a node for an integer constant with value "value" and kind "kind", and return a pointer to it. */ { an_expr_node_ptr node; a_constant_ptr constant = local_constant(); set_integer_constant(constant, (a_host_large_integer)value, kind); node = alloc_node_for_constant(constant); release_local_constant(&constant); return node; } /* node_for_integer_constant */ an_expr_node_ptr node_for_host_large_integer(a_host_large_integer value, an_integer_kind kind) /* Make a node for a host large integer with value "value" and kind "kind", and return a pointer to it. */ { an_expr_node_ptr node; a_constant_ptr constant = local_constant(); set_integer_constant(constant, value, kind); node = alloc_node_for_constant(constant); release_local_constant(&constant); return node; } /* node_for_host_large_integer */ a_boolean is_bad_type_for_template_arg_operand(a_type_ptr type) /* Return TRUE if type is a bad type for an operand of an expression in a template argument, i.e., it is not integral, enum, or a nullptr type. */ { a_boolean is_bad_type; if (is_integral_or_enum_type(type) || is_nullptr_type(type)) { is_bad_type = FALSE; } else if ((gpp_mode || microsoft_mode) && is_floating_type(type)) { /* g++ allows floating-point values in a template argument as long as the final result is integral. Checked in 3.4 - 4.3. Microsoft also, checked in 7.1, 10. */ is_bad_type = FALSE; } else if (is_template_param_type(type)) { /* This comes up in the deduction done to determine if one function is more specialized than another. */ is_bad_type = FALSE; } else if (is_error_type(type)) { is_bad_type = FALSE; } else { is_bad_type = TRUE; } /* if */ return is_bad_type; } /* is_bad_type_for_template_arg_operand */ static a_type_ptr type_of_copied_template_expr(an_expr_node_ptr expr, a_constant *constant, a_constant_ptr alloc_con) /* expr/constant/alloc_con represent a copied template parameter expression in the form used in copy_template_param_expr and its subroutines. Return the type of the expression. */ { a_type_ptr type; if (expr != NULL) { type = expr->type; } else { if (alloc_con != NULL) { type = alloc_con->type; } else { check_assertion(constant != NULL); type = constant->type; } /* if */ if (is_any_reference_type(type)) { type = type_pointed_to(type); } /* if */ } /* if */ return type; } /* type_of_copied_template_expr */ static a_type_ptr unqual_type_of_copied_template_expr( an_expr_node_ptr expr, a_constant *constant, a_constant_ptr alloc_con) /* Similar to type_of_copied_template_expr, but the returned type is stripped of typerefs. */ { a_type_ptr type = type_of_copied_template_expr(expr, constant, alloc_con); type = skip_typerefs(type); return type; } /* unqual_type_of_copied_template_expr */ static void cast_copied_template_param_expr(an_expr_node_ptr *expr, a_constant *constant, a_constant_ptr *alloc_con, a_type_ptr new_type, a_source_position *source_pos) /* expr/constant/alloc_con represent a copied template parameter expression in the form used in copy_template_param_expr and its subroutines. Cast the expression to the type new_type, if necessary. *expr, *alloc_con, and the constant pointed to by constant are updated as needed. *source_pos gives the source position for errors. */ { a_type_ptr curr_type; a_boolean did_not_fold; curr_type = unqual_type_of_copied_template_expr(*expr, constant, *alloc_con); if (!cast_identical_types(curr_type, new_type)) { /* Casting is required. */ if (*expr != NULL) { /* There is already an expression, so add a cast to it. */ *expr = make_operator_node((an_expr_operator_kind)eok_cast, new_type, *expr); (*expr)->compiler_generated = TRUE; } else { if (*alloc_con != NULL) { /* Drop back from an allocated to an unallocated copy of the constant. */ copy_constant(*alloc_con, constant); *alloc_con = NULL; } /* if */ /* Cast the constant to the new type. */ type_change_constant(constant, new_type, /*is_implicit_cast=*/TRUE, /*maintain_expression=*/FALSE, &did_not_fold, source_pos); check_assertion(!did_not_fold); } /* if */ } /* if */ } /* cast_copied_template_param_expr */ static void do_conversions_on_operands_of_copied_template_expr( an_expr_operator_kind op, an_expr_node_ptr *operand_1, a_constant *constant_1, a_constant_ptr *alloc_con_1, a_boolean op_2_present, an_expr_node_ptr *operand_2, a_constant *constant_2, a_constant_ptr *alloc_con_2, a_boolean op_3_present, an_expr_node_ptr *operand_3, a_constant *constant_3, a_constant_ptr *alloc_con_3, a_source_position *source_pos, a_type_ptr *operation_type, a_boolean *copy_error) /* Subroutine for copy_template_param_expr. An operation involving template parameter constants is being processed. Its operator is op, and its operands are *operand_1, *operand_2, and *operand_3. op_2_present and op_3_present indicate the presence of operands after the first. If a given operand is available only as a constant, *operand_n is NULL and the corresponding parameters alloc_con_n and constant_n point to an allocated or unallocated copy of the constant. *operation_type indicates the operation type; it is updated on return. If there is some error (e.g., an operand type is not integral), *copy_error is set to TRUE. *source_pos gives the source position for errors. */ { a_type_ptr type_1, type_2 = NULL, type_3 = NULL; a_type_ptr result_type = *operation_type, promoted_type_2; a_type_ptr usual_conv_type; a_boolean do_promotion, do_usual_arith_conversions; a_boolean bad_types = FALSE; a_boolean result_type_is_bool = FALSE; type_1 = unqual_type_of_copied_template_expr(*operand_1, constant_1, *alloc_con_1); if (op_2_present) { type_2 = unqual_type_of_copied_template_expr(*operand_2, constant_2, *alloc_con_2); if (op_3_present) { type_3 = unqual_type_of_copied_template_expr(*operand_3, constant_3, *alloc_con_3); } /* if */ } else { /* Let Coverity Prevent know that op_3_present cannot be TRUE when op_2_present is FALSE. */ check_assertion(op_3_present == FALSE); } /* if */ if (is_bad_type_for_template_arg_operand(type_1) || (op_2_present && is_bad_type_for_template_arg_operand(type_2)) || (op_3_present && is_bad_type_for_template_arg_operand(type_3))) { if (op == (an_expr_operator_kind)eok_address_of) { /* The operand of "&" can have any type. */ } else if (op == (an_expr_operator_kind)eok_cast && (is_pointer_or_handle_type(type_1) || is_ptr_to_member_type(type_1))) { /* The operand of cast can have a pointer, handle, or pointer-to-member type. */ } else { bad_types = TRUE; } /* if */ } /* if */ if (bad_types) { /* At least one of the operands has an invalid type. Deduction fails. */ subst_fail(*copy_error); } else if (!op_2_present) { /* One-operand operation. */ /* Determine whether the integral promotions should be done for this operation. */ do_promotion = FALSE; switch (op) { case eok_negate: case eok_unary_plus: { a_type_kind type_kind = skip_typerefs(type_1)->kind; do_promotion = type_kind == (a_type_kind)tk_integer; } break; case eok_complement: do_promotion = TRUE; break; default: break; } /* switch */ if (do_promotion) { result_type = type_after_integral_promotion(type_1); cast_copied_template_param_expr(operand_1, constant_1, alloc_con_1, result_type, source_pos); } else if (op == (an_expr_operator_kind)eok_not) { /* The result of !x is always bool, regardless of the type of x. */ result_type = bool_type(); } /* if */ } else if (!op_3_present) { a_boolean assignment_case = FALSE; /* Two-operand operation. */ do_usual_arith_conversions = do_promotion = FALSE; switch (op) { case eok_add: case eok_subtract: case eok_multiply: case eok_divide: case eok_and: case eok_or: case eok_xor: case eok_remainder: do_usual_arith_conversions = TRUE; break; case eok_shiftl: case eok_shiftr: do_promotion = TRUE; break; case eok_eq: case eok_ne: case eok_gt: case eok_lt: case eok_ge: case eok_le: if (!is_nullptr_type(type_1) && !is_nullptr_type(type_2)) { /* The usual arithmetic conversions are not performed if one of the operands has a nullptr type. */ do_usual_arith_conversions = TRUE; } /* if */ /* The result of a comparison is always bool, regardless of the types of the operands. */ result_type_is_bool = TRUE; break; #if GNU_EXTENSIONS_ALLOWED case eok_gnu_min: case eok_gnu_max: if (!is_nullptr_type(type_1) && !is_nullptr_type(type_2)) { /* The usual arithmetic conversions are not performed if one of the operands has a nullptr type. */ do_usual_arith_conversions = TRUE; } /* if */ break; #endif /* GNU_EXTENSIONS_ALLOWED */ case eok_add_assign: case eok_subtract_assign: case eok_multiply_assign: case eok_divide_assign: case eok_remainder_assign: case eok_and_assign: case eok_or_assign: case eok_xor_assign: do_usual_arith_conversions = TRUE; assignment_case = TRUE; break; case eok_shiftl_assign: case eok_shiftr_assign: do_promotion = TRUE; assignment_case = TRUE; break; #if C99_IL_EXTENSIONS_SUPPORTED /* The following imaginary type operators are used only in C mode. If they are added for C++ mode, bear in mind that determine_arithmetic_conversions does not do the whole job for imaginary types; see also determine_imaginary_operation_type. */ case eok_jmultiply: case eok_jdivide: unexpected_condition_str("imaginary operators not implemented"); break; #endif /* C99_IL_EXTENSIONS_SUPPORTED */ default: break; } /* switch */ if (do_usual_arith_conversions) { usual_conv_type = usual_arithmetic_conversions(type_1, type_2); if (!assignment_case) { cast_copied_template_param_expr(operand_1, constant_1, alloc_con_1, usual_conv_type, source_pos); } /* if */ cast_copied_template_param_expr(operand_2, constant_2, alloc_con_2, usual_conv_type, source_pos); if (result_type_is_bool) { result_type = bool_type(); } else { result_type = usual_conv_type; } /* if */ } else if (do_promotion) { result_type = type_after_integral_promotion(type_1); if (!assignment_case) { cast_copied_template_param_expr(operand_1, constant_1, alloc_con_1, result_type, source_pos); } /* if */ promoted_type_2 = type_after_integral_promotion(type_2); cast_copied_template_param_expr(operand_2, constant_2, alloc_con_2, promoted_type_2, source_pos); } /* if */ } else if (op == (an_expr_operator_kind)eok_question) { /* Three-operand operation, i.e., "?" */ /* If the operands have the same type, use that type. Otherwise, if one of the operands has a nullptr type, the result type will also be a nullptr type. Otherwise, do the usual arithmetic conversions. */ if (!types_are_compatible(type_2, type_3)) { if (is_nullptr_type(type_2) || is_nullptr_type(type_3)) { /* The Microsoft C++/CLI compiler follows the third operand in deciding whether the common type is managed or standard. (We will get std::nullptr_t if the third operand does not have a nullptr type, and that's correct, since the second operand must necessarily have been std::nullptr_t -- the managed nullptr type is incompatible with non-nullptr operands.) */ if (is_managed_nullptr_type(type_3)) { result_type = managed_nullptr_type(); } else { result_type = standard_nullptr_type(); } /* if */ } else { result_type = usual_arithmetic_conversions(type_2, type_3); } /* if */ cast_copied_template_param_expr(operand_2, constant_2, alloc_con_2, result_type, source_pos); cast_copied_template_param_expr(operand_3, constant_3, alloc_con_3, result_type, source_pos); } /* if */ } /* if */ *operation_type = result_type; } /* do_conversions_on_operands_of_copied_template_expr */ static an_expr_node_ptr alloc_copied_template_param_expr( an_expr_node_ptr expr, a_constant *constant, a_constant_ptr alloc_con) /* Return a pointer to an expression for a copied template parameter expression. If expr is non-NULL, the expression already exists and the value of expr is returned. Otherwise, an expression for a constant must be created and a pointer to that returned. If alloc_con is non-NULL, it is a pointer to an already-allocated constant; otherwise, constant points to the (unallocated) constant value. */ { if (expr == NULL) { a_type_ptr ctp; if (alloc_con != NULL) { expr = alloc_node_for_allocated_constant(alloc_con); ctp = alloc_con->type; } else { expr = alloc_node_for_constant(constant); ctp = constant->type; } /* if */ if (is_any_reference_type(ctp)) { expr->is_lvalue = TRUE; check_assertion(!is_rvalue_reference_type(ctp)); } /* if */ } /* if */ return expr; } /* alloc_copied_template_param_expr */ static a_boolean operator_is_foldable(an_expr_node_ptr expr) /* Return TRUE if the indicated enk_operation expression node should be folded when doing template argument substitution. Note that this applies to old-style SFINAE, and not the new-style SFINAE of C++11. See expr_is_rescannable for the equivalent routine for new-style SFINAE. */ { a_boolean is_foldable = FALSE; an_expr_operator_kind op; check_assertion(is_operation_node(expr)); op = expr->variant.operation.kind; switch (op) { case eok_negate: case eok_unary_plus: case eok_add: case eok_subtract: case eok_multiply: case eok_divide: case eok_remainder: case eok_eq: case eok_ne: case eok_gt: case eok_lt: case eok_ge: case eok_le: is_foldable = node_operator_type_kind_is(expr, tk_integer) || node_operator_type_kind_is(expr, tk_template_param); break; case eok_address_of: case eok_complement: case eok_not: case eok_shiftl: case eok_shiftr: case eok_and: case eok_or: case eok_xor: case eok_land: case eok_lor: case eok_question: case eok_cast: #if GNU_EXTENSIONS_ALLOWED case eok_gnu_min: case eok_gnu_max: #endif /* GNU_EXTENSIONS_ALLOWED */ case eok_parens: case eok_splice: is_foldable = TRUE; break; default: break; } /* switch */ return is_foldable; } /* operator_is_foldable */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_boolean is_cast_operation_node(an_expr_node_ptr expr) /* Return TRUE if the specified node is an operation node reflecting some kind of cast. */ { a_boolean is_cast = FALSE; if (is_operation_node(expr)) { switch (expr->variant.operation.kind) { case eok_cast: case eok_lvalue_cast: case eok_ref_cast: case eok_lvalue_adjust: case eok_box: case eok_unbox: case eok_base_class_cast: case eok_derived_class_cast: case eok_pm_base_class_cast: case eok_pm_derived_class_cast: case eok_dynamic_cast: case eok_ref_dynamic_cast: case eok_bool_cast: is_cast = TRUE; break; default: break; } /* switch */ } /* if */ return is_cast; } /* is_cast_operation_node */ a_boolean is_generated_dynamic_init(a_dynamic_init_ptr dip) /* Return TRUE if the given dynamic init entry is a compiler-generated one, i.e., not the result of an explicit source operation like a cast. */ { a_boolean is_generated = !(dip->is_explicit_cast || dip->is_compound_literal || dip->is_braced_initializer); return is_generated; } /* is_generated_dynamic_init */ a_boolean is_error_dynamic_init(a_dynamic_init_ptr dip) /* Return TRUE if the given dynamic init entry represents an error. */ { a_boolean err = FALSE; switch (dip->kind) { case dik_constant: err = is_error_constant(dip->variant.constant.ptr); break; case dik_expression: err = is_error_node(dip->variant.expression); break; default: break; } /* switch */ return err; } /* is_error_dynamic_init */ #if !STANDALONE_UTILITY_PROGRAM static an_expr_node_ptr copy_template_param_expr_as_lvalue( an_expr_node_ptr expr, a_template_arg_ptr template_arg_list, a_template_param_ptr template_param_list, a_type_ptr guide_type, a_source_position *source_pos, a_ctws_options_set options, a_boolean *copy_error, a_ctws_state_ptr ctws_state) /* Copy the expression expr, doing substitution for template parameters, and return a pointer to the copy. The expression will be used as an lvalue, so convert it to an lvalue if necessary, or fail if an lvalue cannot be produced. See copy_template_param_expr for the parameter descriptions. Because lvalues are always represented as expressions and not constants, this routine does not have the constant-value parameters that are usual in these template-parameter-substitution routines. */ { an_expr_node_ptr expr_copy = NULL; if (is_error_node(expr)) { expr_copy = error_node(); } else if (expr->is_lvalue || is_template_dependent_type(expr->type)) { /* The expression is already an lvalue. Just copy with substitution and return. */ a_constant_ptr constant = local_constant(); a_constant_ptr alloc_con; expr_copy = copy_template_param_expr(expr, template_arg_list, template_param_list, guide_type, source_pos, options, copy_error, ctws_state, constant, &alloc_con); /* Force the copy to expression form. */ expr_copy = alloc_copied_template_param_expr(expr_copy, constant, alloc_con); release_local_constant(&constant); } else { /* The expression is an rvalue. There's no way to convert it to an lvalue. Fail. */ subst_fail(*copy_error); } /* if */ if (*copy_error) { /* Return an error node on a copy error. */ expr_copy = error_node(); } /* if */ return expr_copy; } /* copy_template_param_expr_as_lvalue */ static an_expr_node_ptr copy_template_param_expr_as_rvalue( an_expr_node_ptr expr, a_template_arg_ptr template_arg_list, a_template_param_ptr template_param_list, a_type_ptr guide_type, a_source_position *source_pos, a_ctws_options_set options, a_boolean *copy_error, a_ctws_state_ptr ctws_state, a_constant_ptr constant, a_constant_ptr *alloc_con) /* Copy the expression expr, doing substitution for template parameters, and return a pointer to the copy. The expression will be used as an rvalue, so convert it to an rvalue if necessary, or fail if an rvalue cannot be produced. See copy_template_param_expr for the parameter descriptions. */ { an_expr_node_ptr expr_copy; *alloc_con = NULL; if (is_error_node(expr)) { expr_copy = error_node(); } else { /* Make a copy, doing substitution. */ expr_copy = copy_template_param_expr(expr, template_arg_list, template_param_list, guide_type, source_pos, options, copy_error, ctws_state, constant, alloc_con); if (expr_copy != NULL && expr_copy->is_lvalue) { /* The expression is an lvalue. Try to convert it to an rvalue. */ if (is_array_type(expr_copy->type)) { /* Convert an array to a constant pointer. */ if (constant_glvalue_address(expr_copy, constant, /*address_escapes=*/TRUE)) { a_type_ptr ptr_type = type_after_array_to_pointer_transformation( expr_copy->type); implicit_cast(constant, ptr_type); expr_copy = NULL; } else { /* The array does not have a constant address. */ expr_copy = conv_array_expr_to_pointer(expr_copy); } /* if */ } else if (is_function_type(expr_copy->type)) { /* Convert a function to a constant pointer. */ if (constant_glvalue_address(expr_copy, constant, /*address_escapes=*/TRUE)) { a_type_ptr ptr_type = make_pointer_type(expr_copy->type); implicit_cast(constant, ptr_type); expr_copy = NULL; } else { /* The function does not have a constant address. */ expr_copy = rvalue_expr_for_lvalue(expr_copy); } /* if */ } else { /* Other kind of lvalue (not an array or function). */ a_boolean constant_case; expr_copy = conv_glvalue_expr_to_prvalue_external(expr_copy, &constant_case, alloc_con, source_pos, options); } /* if */ } else if (expr_copy != NULL) { if (is_template_dependent_type(expr_copy->type) || !fold_expr(expr_copy, constant)) { make_template_param_expr_constant(expr_copy, constant); } /* if */ } else if (constant != NULL && constant_is(constant, ck_template_param) && constant->variant.template_param.kind == (a_template_param_constant_kind)tpck_expression) { /* In some cases a tpck_expression entry is created because the expression is not foldable as an lvalue, but it could be folded as an rvalue. */ an_expr_node_ptr fold_expr = constant->variant.template_param.variant.expr; if (is_glvalue_node(fold_expr) && depth_template_declaration_scope == NO_SCOPE_DEPTH && !scope_stack_top().in_prototype_instantiation) { a_constant_ptr fold_con = constant_value_addressed_by_node(fold_expr); if (fold_con != NULL) { expr_copy = NULL; copy_constant(fold_con, constant); } /* if */ } /* if */ } /* if */ } /* if */ if (*copy_error) { /* Return an error node on a copy error. */ expr_copy = error_node(); *alloc_con = NULL; } /* if */ return expr_copy; } /* copy_template_param_expr_as_rvalue */ static an_expr_node_ptr copy_template_param_expr_list( an_expr_node_ptr args, a_template_arg_ptr template_arg_list, a_template_param_ptr template_param_list, a_source_position *source_pos, a_ctws_options_set options, a_boolean *copy_error, a_ctws_state_ptr ctws_state) /* Copy an expression list, given by args, and return a pointer to the copy, as part of a template argument expression. See copy_template_param_expr for the parameter descriptions. */ { an_expr_node_ptr new_args = NULL; an_expr_node_ptr arg = args, *new_arg = &new_args; a_constant_ptr const_result = local_constant(); a_constant_ptr alloc_const_result; while (arg != NULL) { *new_arg = copy_template_param_expr( arg, template_arg_list, template_param_list, (a_type_ptr)NULL, source_pos, options, copy_error, ctws_state, const_result, &alloc_const_result); if (*copy_error) break; *new_arg = alloc_copied_template_param_expr(*new_arg, const_result, alloc_const_result); arg = arg->next; new_arg = &((*new_arg)->next); } /* while */ release_local_constant(&const_result); return new_args; } /* copy_template_param_expr_list */ static an_expr_node_ptr copy_template_param_builtin_operation( an_expr_node_ptr expr, a_template_arg_ptr template_arg_list, a_template_param_ptr template_param_list, a_source_position *source_pos, a_ctws_options_set options, a_boolean *copy_error, a_ctws_state_ptr ctws_state, a_constant_ptr constant) /* Copy the expression expr, which has an enk_builtin_operation on top, and return a pointer to the copy. The expression is part of a template argument expression. See copy_template_param_expr for the parameter descriptions. */ { an_expr_node_ptr args = expr->variant.builtin_operation.operands; an_expr_node_ptr expr_copy = NULL, new_args; /* First copy the argument list with any necessary substitutions. */ new_args = copy_template_param_expr_list(args, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state); if (!*copy_error) { a_boolean not_a_constant; /* Copy the expression node and attach the copied argument list to it. */ expr_copy = copy_node(expr); expr_copy->variant.builtin_operation.operands = new_args; /* Attempt to fold the operation. */ fold_builtin_operation_if_possible(expr_copy, constant, /*maintain_expression=*/FALSE, (a_source_position*)NULL, ¬_a_constant); /* Currently, an operation that doesn't always produce a constant causes copy_template_param_expr to set *copy_error to TRUE. */ check_assertion(!not_a_constant); if (is_error_constant(constant)) { subst_fail(*copy_error); expr_copy = NULL; } else if (constant->kind == (a_constant_repr_kind)ck_template_param) { /* The result is still dependent. Just return the expression node. */ } else { /* The operation was successfully folded into a constant. Do not return an expression. */ expr_copy = NULL; } /* if */ } /* if */ return expr_copy; } /* copy_template_param_builtin_operation */ static a_boolean template_nullptr_operation_types_are_compatible( a_type_ptr type_1, a_constant_ptr con_1, a_type_ptr type_2, a_constant_ptr con_2) /* Return TRUE if two operands of the specified types (at least one of which must be a nullptr type) and, if the operands are constant, with the specified values, can be used together in an expression. This is called from check_template_nullptr_operation to determine whether an expression is permitted in a template argument expression. It is similar to check_compatibility_of_nullptr_operands; the principal difference is that the latter does not accept pointer and pointer-to-member types, which must be handled separately by the caller, while this function handles pointer and pointer-to-member types directly. */ { a_boolean compatible_types; check_assertion(is_nullptr_type(type_1) || is_nullptr_type(type_2)); if (is_nullptr_type(type_1)) { compatible_types = (is_nullptr_type(type_2) || is_pointer_or_handle_type(type_2) || is_ptr_to_member_type(type_2) || (con_2 != NULL && is_standard_nullptr_type(type_1) && is_null_pointer_constant(con_2))); } else { compatible_types = (is_pointer_or_handle_type(type_1) || is_ptr_to_member_type(type_1) || (con_1 != NULL && is_standard_nullptr_type(type_2) && is_null_pointer_constant(con_1))); } /* if */ return compatible_types; } /* template_nullptr_operation_types_are_compatible */ static void check_template_nullptr_operation(an_expr_operator_kind op, a_type_ptr op1_type, a_constant_ptr op1_con, a_type_ptr op2_type, a_constant_ptr op2_con, a_type_ptr op3_type, a_constant_ptr op3_con, a_boolean *copy_error) /* Called from copy_template_param_expr to determine whether an operation involving operands with a nullptr type is permissible in an expression that is the result of substituting template arguments into a template argument expression. The types of the operands are given by the opN_type parameters, and if the operand is a constant, its value is given by the opN_con parameters (NULL if the value is a non-constant expression). If op is an operation that is not permitted on an operand with a nullptr type, or if the type or value of the other operand is not compatible with the nullptr type, set *copy_error to TRUE. (No checking is needed or done if *copy_error is already TRUE.) */ { if (!*copy_error) { switch (op) { case eok_address_of: case eok_cast: case eok_parens: case eok_not: case eok_land: case eok_lor: case eok_comma: /* These are okay with no restrictions. */ break; case eok_eq: case eok_ne: case eok_gt: case eok_lt: case eok_ge: case eok_le: if (!template_nullptr_operation_types_are_compatible(op1_type, op1_con, op2_type, op2_con)) { subst_fail(*copy_error); } /* if */ break; case eok_question: if (!template_nullptr_operation_types_are_compatible(op2_type, op2_con, op3_type, op3_con)) { subst_fail(*copy_error); } /* if */ break; default: /* All other operators are not permitted at all or are not permitted with an operand having a nullptr type. */ subst_fail(*copy_error); break; } /* switch */ } /* if */ } /* check_template_nullptr_operation */ a_boolean is_valid_object_for_nontype_arg(a_constant_ptr con) /* Return TRUE if the given address constant refers to an object that is valid as a nontype template argument. This must be a complete object in pre-C++20 modes, but can be a subobject in C++20 and later. */ { a_boolean result = TRUE; a_targ_size_t num_elements = 1; a_subobject_path_ptr path; a_type_ptr tp; a_variable_ptr var; check_assertion(constant_is(con, ck_address) && address_base_is(con, abk_variable)); var = con->variant.address.variant.variable; tp = skip_typerefs(var->type); path = con->variant.address.subobject_path; if (path != NULL && !cpp20_mode) { /* Prior to C++20, subobjects were never valid as nontype template arguments. Consider: template struct S {}; char arr[3]; char var; S<&arr[0]> x; S<(&arr)[0]> y; S<&(&var)[0]> z; x and y are clearly invalid by the C++17 standard wording since they involve pointers to elements of an array (i.e., subobjects). z is plausibly okay although the standard treats non-array variables as arrays of length 1 for pointer-arithmetic purposes; from that perspective an argument could be made that z is invalid as well. Clang accepts all three. GCC rejects only x. Since GCC's behavior would be more difficult to emulate, we approximate it by using Clang's behavior instead. MSVC rejects all three, but accepts z if var is const and initialized. */ if (!(microsoft_bugs && !var->constant_valued) && path->next == NULL && path->is_offset && path->variant.ptr_offset == 0 && /* A single [0] subscript. */ (clang_mode || gpp_mode || !type_is(tp, tk_array))) { /* Accept these cases as described above. */ } else { result = FALSE; } /* if */ goto done; } /* if */ if (type_is(tp, tk_array) && !is_incomplete_array_type(tp)) { num_elements = num_array_elements(tp); } /* if */ while (result && path != NULL) { if (path->is_offset) { /* Make sure we are pointing to an element of the array/object (or one position past such). */ result = path->variant.ptr_offset >= 0 && (a_targ_size_t)path->variant.ptr_offset <= num_elements; } else if (path->is_base_class) { num_elements = 1; } else { a_field_ptr fp = path->variant.field; tp = skip_typerefs(fp->type); if (type_is(tp, tk_array)) { num_elements = num_array_elements(tp); } else { num_elements = 1; } /* if */ } /* if */ path = path->next; } /* while */ done: return result; } /* is_valid_object_for_nontype_arg */ a_variable_ptr variable_designated_by_object_reflection(a_constant_ptr con) /* If con is an object reflection (a ck_address constant, marked is_object_reflection, whose type is the type of the object it addresses rather than a pointer type, as produced by std::meta::reflect_object and by std::meta::reflect_constant for a value of class type) that designates a complete variable rather than a subobject of one, return that variable. Otherwise return NULL. */ { a_variable_ptr result = NULL; if (constant_is(con, ck_address) && con->variant.address.is_object_reflection && address_base_is(con, abk_variable) && con->variant.address.offset == 0 && con->variant.address.subobject_path == NULL) { result = con->variant.address.variant.variable; } /* if */ return result; } /* variable_designated_by_object_reflection */ static a_constant_ptr nontype_arg_base_constant(a_constant_ptr con) /* If con is a template parameter constant used as a template argument but an underlying ("base") constant should be used to check if it is valid as a template argument, return that underlying constant. Otherwise return con. */ { a_constant_ptr base_con = con; if (constant_is(con, ck_template_param)) { a_boolean explicit_cast; if (tpck_is(con, tpck_expression) && !is_template_param_cast_constant(con, &base_con, &explicit_cast)) { an_expr_node_ptr expr = expr_node_from_tpck_expression(con); if (expr != NULL && is_operation_node(expr) && node_operator_is(expr, eok_array_to_pointer)) { an_expr_node_ptr opnd = expr->variant.operation.operands; if (is_constant_node(opnd)) { base_con = node_constant(opnd); } /* if */ } /* if */ } else if (tpck_is(con, tpck_dependent_constant)) { base_con = con->variant.template_param.variant.constant; } else if (tpck_is(con, tpck_concat_string_literals)) { base_con = con->variant.template_param.variant.string_literal_list; } /* if */ } /* if */ return base_con; } /* nontype_arg_base_constant */ a_boolean is_valid_ptr_or_ptr_to_member_templ_arg_constant(a_constant_ptr con) /* Return TRUE if the given constant represents a valid pointer or pointer-to- member template argument. */ { a_boolean result = FALSE; a_boolean null_value_okay = !strict_ansi_mode || cpp11_mode || null_template_ptr_arg_enabled || (ms_extensions && microsoft_version >= 1800); /* The C++11 standard allows not only "null pointer constants", but, more generally, "null pointer values" (which can result from casting a null pointer constant to a pointer type). Microsoft compilers also allow things like "&typeid(X)" and "&__uuidof(X)". */ while (constant_is(con, ck_template_param)) { a_constant_ptr base_con = nontype_arg_base_constant(con); if (base_con == con) break; con = base_con; } /* while */ if (constant_is(con, ck_template_param)) { result = TRUE; } else if (is_null_pointer_constant(con)) { result = null_value_okay; } else if (constant_is(con, ck_address)) { if (ms_extensions && (address_base_is(con, abk_typeid) || address_base_is(con, abk_uuidof))) { result = TRUE; } else if (address_base_is(con, abk_routine)) { result = con->variant.address.variant.routine == NULL ? null_value_okay : TRUE; } else if (address_base_is(con, abk_variable)) { if (con->variant.address.variant.variable == NULL) { result = null_value_okay; } else { /* Check that this variable address constant is valid as a nontype argument. Older versions of GCC accept subobject addresses in pre-C++20 modes. */ result = is_valid_object_for_nontype_arg(con) || (con->variant.address.offset == 0 && (gnu_version_is(<30400))); } /* if */ } /* if */ } else if (constant_is(con, ck_ptr_to_member)) { result = (con->variant.ptr_to_member.is_function_ptr ? con->variant.ptr_to_member.variant.routine == NULL : con->variant.ptr_to_member.variant.field == NULL) ? null_value_okay : TRUE; } else if (null_value_okay && constant_is(con, ck_integer)) { /* A "zero" constant converted to a pointer or pointer-to-member type is acceptable. In Microsoft mode, a non-zero constant converted to a pointer type is acceptable, too. */ a_type_ptr tp = skip_typerefs(con->type); if ((is_pointer_or_handle_type(tp) || is_ptr_to_member_type(tp)) && cmplit_integer_constant(con, (a_host_large_integer)0) == 0) { result = TRUE; } else if (microsoft_mode && is_pointer_type(tp)) { result = TRUE; } /* if */ } /* if */ return result; } /* is_valid_ptr_or_ptr_to_member_templ_arg_constant */ a_boolean is_valid_class_templ_arg_constant(a_constant_ptr con) /* con is the value for a nontype template argument of class type (permitted in C++20). Return FALSE if the value is not an aggregate value, or if any pointer, reference, or pointer-to-member constants it contains are invalid. Otherwise, return TRUE. */ { a_boolean result = TRUE; if (!constant_is(con, ck_template_param)) { a_constant_ptr cp; if (!constant_is(con, ck_aggregate)) { /* The value of an argument of class type is an aggregate value. A constant of some other kind gets here from a splice of a reflection that designates a subobject: its value is an address rather than the value of the object it designates. */ result = FALSE; goto done; } /* if */ cp = con->variant.aggregate.first_constant; for (; cp != NULL; cp = cp->next) { switch (cp->kind) { case ck_address: case ck_ptr_to_member: if (!is_valid_ptr_or_ptr_to_member_templ_arg_constant(cp)) { result = FALSE; goto done; } /* if */ break; case ck_aggregate: if (!is_valid_class_templ_arg_constant(cp)) { result = FALSE; goto done; } /* if */ break; default: break; } /* switch */ } /* for */ } /* if */ done: return result; } /* is_valid_class_templ_arg_constant */ a_boolean is_valid_templ_arg_constant(a_constant_ptr con) /* Return FALSE if this is a pointer, reference, or pointer-to-member constant that is not valid as a template argument, or if it is an aggregate constant that (directly or indirectly) contains such an invalid constant. Otherwise, return TRUE. */ { a_boolean result; if (constant_is(con, ck_aggregate)) { result = is_valid_class_templ_arg_constant(con); } else if (constant_is(con, ck_address) || constant_is(con, ck_ptr_to_member) || is_ptr_or_ref_type(con->type)) { result = is_valid_ptr_or_ptr_to_member_templ_arg_constant(con); } else { result = TRUE; } /* if */ return result; } /* is_valid_templ_arg_constant */ static a_boolean substituted_cast_is_valid(a_constant *src_con, a_type_ptr new_type, a_boolean is_explicit_cast, a_boolean for_templ_arg, a_boolean *reinterpret_cast_needed) /* A cast has been subjected to substitution of template arguments. It comes down to a cast of the constant src_con to the type new_type. The cast is explicit if is_explicit_cast is TRUE. The conversion is for a nontype template argument is for_templ_arg is TRUE. Return TRUE if the cast is valid. If a reinterpret_cast is needed to do the cast, return *reinterpret_cast_needed TRUE. */ { a_boolean valid = FALSE; a_std_conv_descr std_conv; an_error_code warning_suggested; /* Note that this routine is imperfect for explicit casts; we don't know the exact kind of cast. However, this routine's processing for explicit casts is mostly for historical compatibility. When modern SFINAE rules are enabled (cpp11_sfinae_enabled is TRUE), explicit casts are rescanned in normal expression processing, and all the right checking is done. So the imperfections here are not important, and may in fact be correct for compatibility with older code. */ *reinterpret_cast_needed = FALSE; if (is_void_type(new_type)) { /* valid = FALSE; */ } else if (is_any_reference_type(new_type)) { /* Implicit casts on nontype template arguments can produce casts to reference types that are valid if the underlying constant ends up being the address of a function or namespace-scope variable. */ if (!is_explicit_cast && constant_is(src_con, ck_address) && identical_types(new_type, src_con->type)) { valid = TRUE; } /* if */ } else if (is_explicit_cast ? expl_conversion_possible(src_con->type, /*source_is_constant=*/TRUE, /*source_is_string_literal=*/ FALSE, /*source_is_function=*/FALSE, src_con, new_type, reinterpret_cast_needed, ec_bad_cast, &warning_suggested) : impl_conversion_possible(src_con->type, /*source_is_constant=*/TRUE, /*source_is_string_literal=*/ FALSE, /*source_is_function=*/FALSE, /*is_copy_initialization=*/FALSE, src_con, new_type, /*singleton_braced_init=*/FALSE, /*allow_qualifier_or_eh_mismatch=*/FALSE, /*suppress_extensions=*/FALSE, ec_bad_cast, &std_conv)) { if (for_templ_arg && !is_explicit_cast) { valid = conversion_allowed_for_nontype_template_argument( &std_conv, src_con->type, /*source_is_constant=*/TRUE, src_con, new_type, (an_error_code*)NULL); } else { valid = TRUE; } /* if */ } /* if */ return valid; } /* substituted_cast_is_valid */ static an_expr_node_ptr copy_template_param_lambda_expr( an_expr_node_ptr expr, a_template_arg_ptr template_arg_list, a_template_param_ptr template_param_list, a_source_position *source_pos, a_ctws_options_set options, a_boolean *copy_error, a_ctws_state_ptr ctws_state) /* Substitute template parameters in the lambda expression expr by re-scanning the tokens of the lambda that were cached when the lambda was first scanned (see scan_lambda in class_decl.c). This creates a new closure class and a new enk_lambda expression node. Return the copied expression, or NULL if the copy fails (in which case *copy_error is set to TRUE). See copy_template_param_expr for the parameter descriptions. */ { an_expr_node_ptr expr_copy = NULL; a_lambda_ptr lambda = expr->variant.init.source.lambda; a_type_ptr closure_class = lambda->closure_class; a_token_cache_ptr tokens; /* First, do a preliminary substitution on the lambda's routine type so that any SFINAE-able substitution failure is reported as a deduction failure here rather than as a hard error during the re-scanning of the tokens. */ (void)copy_type_with_substitution(lambda->lambda_routine->type, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state); if (*copy_error) goto done; tokens = cached_lambdas->get(closure_class); if (tokens == NULL) { /* No cached tokens were recorded for this lambda (this can happen in severe error cases). Substitution cannot proceed. */ subst_fail(*copy_error); } else { an_operand result_operand; an_expr_stack_entry expr_stack_entry; an_expr_stack_entry_ptr saved_expr_stack; a_boolean pushed_template_decl_scope = FALSE; a_scope_ptr lambda_enclosing_scope = NULL; a_scope_depth scope_depth_before_local_reactivation = NO_SCOPE_DEPTH; /* Set up a fresh expression and lexical context so that the lambda tokens can be rescanned without interfering with the surrounding state, then re-parse the lambda as a full expression. */ save_expr_stack(&saved_expr_stack); push_expr_stack(ek_normal, &expr_stack_entry, /*force_object_lifetime=*/FALSE, /*suppress_object_lifetime=*/TRUE); expr_stack_entry.template_deduction_context = TRUE; expr_stack_entry.suppress_diagnostics = TRUE; expr_stack_entry.possible_rescan_context = TRUE; push_lexical_state_stack(); if (template_param_list != NULL && template_param_list->cache.decl_info != NULL && template_param_list->param_symbol != NULL) { /* Make the template parameters being substituted visible during the rescan so that names that refer to them can be resolved. The scope is pushed reusing the original declaration scope number so that the template parameter symbols are visible to ordinary name lookup. */ push_template_declaration_scope_full( template_param_list->cache.decl_info, template_param_list->param_symbol->decl_scope, /*is_template_template_param=*/FALSE, /*is_template_param_rescan=*/FALSE); scope_stack_top().is_reactivation = TRUE; pushed_template_decl_scope = TRUE; /* Force the template parameter symbols to refer to the values specified by the actual template arguments so that name lookup of those parameters during the rescan returns the substituted values directly (their state will be restored by the enclosing instantiation scope when it is left). */ update_template_param_symbols(template_param_list, template_arg_list); } /* if */ /* If the lambda was originally declared in a local scope (i.e., inside a function body), reactivate that scope so that local variables and other names declared in the enclosing function are visible to ordinary name lookup during the rescan. This mirrors the treatment of generic lambdas, where the enclosing local scope is reactivated when the call operator is instantiated (see the use of PS_IS_GENERIC_LAMBDA in push_template_instantiation_scope). The lambda's stored parent scope is the one recorded when the lambda was originally scanned -- for a lambda inside a template, that is the template's function scope, not the current instantiation. To reactivate local variables from the enclosing function as they exist during this instantiation, find the innermost local scope that is currently on the scope stack; that is the scope for the instance of the enclosing routine that is presently being processed. */ lambda_enclosing_scope = get_parent_scope_of(closure_class); if (lambda_enclosing_scope != NULL && is_local_scope_kind(lambda_enclosing_scope->kind)) { a_scope_depth d; a_scope_ptr active_enclosing_scope = NULL; a_routine_ptr enclosing_rp = NULL; for (d = depth_scope_stack; d != NO_SCOPE_DEPTH; d--) { a_scope_stack_entry_ptr ssep = &scope_stack[d]; if (is_local_scope_kind(ssep->kind) && !ssep->is_reactivation && ssep->il_scope != NULL) { active_enclosing_scope = ssep->il_scope; if (scope_is(active_enclosing_scope, sck_function)) { enclosing_rp = active_enclosing_scope->variant.routine.ptr; } /* if */ break; } /* if */ if (d == 0) break; } /* for */ if (active_enclosing_scope != NULL) { an_expr_stack_entry_ptr saved_curr_expr_stack = expr_stack; scope_depth_before_local_reactivation = depth_scope_stack; reactivate_local_context(/*decl_info=*/(a_template_decl_info_ptr)NULL, active_enclosing_scope, /*instance_sym=*/NULL, /*assoc_type=*/NULL, enclosing_rp, PS_NO_OPTIONS); /* push_scope_full clears the global expression stack when it pushes an sck_function scope. Since we are rescanning in an already-active expression context, restore it. */ expr_stack = saved_curr_expr_stack; } /* if */ } /* if */ rescan_persistent_reusable_cache(tokens); scan_lambda_expression(&result_operand); if (is_immediate_class_type(result_operand.type) && is_lambda_closure_type(result_operand.type)) { /* Pushing a template declaration scope above (to reactivate the template parameters) unfortunately causes make_closure_class to mark the closure as nonreal. In some situations with class template argument deduction, we get a spuriously nonreal class even when we didn't push the template declaration scope above. Just reset the flag here. */ result_operand.type->variant.class_struct_union.is_nonreal_class = FALSE; } /* if */ flush_past_token_cache_terminator(); if (scope_depth_before_local_reactivation != NO_SCOPE_DEPTH) { /* Pop the reactivation scopes that were pushed above. */ while (depth_scope_stack > scope_depth_before_local_reactivation) { pop_scope(); } /* while */ } /* if */ if (pushed_template_decl_scope) { pop_scope(); } /* if */ pop_lexical_state_stack(); pop_expr_stack(); restore_expr_stack(saved_expr_stack); if (is_error_operand(&result_operand)) { subst_fail(*copy_error); } else { expr_copy = expr_node_from_operand(&result_operand); if (expr_copy == NULL || !node_is(expr_copy, enk_lambda)) { /* The re-scanned lambda is malformed. */ subst_fail(*copy_error); } /* if */ } /* if */ } /* if */ done: return expr_copy; } /* copy_template_param_lambda_expr */ an_expr_node_ptr copy_template_param_expr( an_expr_node_ptr expr, a_template_arg_ptr template_arg_list, a_template_param_ptr template_param_list, a_type_ptr guide_type, a_source_position *source_pos, a_ctws_options_set options, a_boolean *copy_error, a_ctws_state_ptr ctws_state, a_constant_ptr constant, a_constant_ptr *alloc_con) /* Copy the expression expr, and return a pointer to the copy. The expression is part of a template deduction context, and is usually but not always a constant expression (see CTWS_NON_CONSTANT_EXPR). In the process of copying, replace any template parameters with the corresponding values from the template argument list template_arg_list. template_param_list is the parameter list for which template_arg_list is an argument list. If guide_type is non-NULL, it is a "guide" type for the expression, either the type of the template parameter or the destination type of a cast above this constant. source_pos provides the source position for any calls of copy_type_with_substitution. If there is an error in the copying (e.g., if there is an error in doing substitution on a type), set *copy_error to TRUE. If the expression after substitution is a constant, set *alloc_con to the address of the constant and return NULL. If there is no allocated copy of the constant, set *alloc_con to NULL, set *constant to the constant value, and return NULL. options is a set of substitution options. */ { an_expr_node_ptr expr_copy = NULL; an_expr_operator_kind op; an_error_code error_detected; #if CHECKING a_boolean non_constant_expr = (options & CTWS_NON_CONSTANT_EXPR) != 0; #endif /* CHECKING */ a_constant_ptr constant_1 = local_constant(); a_constant_ptr constant_2 = local_constant(); a_constant_ptr constant_3 = local_constant(); *alloc_con = NULL; if (cpp11_sfinae_enabled && expr_is_rescannable(expr)) { /* Redo the semantic analysis on the expression, after substitution. This makes a copy of the expression, even of parts that are not changed by substitution, so the original expression remains unchanged. */ a_rescan_control_block rcblock; clear_rescan_control_block(&rcblock); rcblock.template_arg_list = template_arg_list; rcblock.template_param_list = template_param_list; rcblock.options = options; rcblock.ctws_state = ctws_state; expr_copy = rescan_expr_with_substitution(expr, guide_type, &rcblock, constant); if (rcblock.error_detected) { /* There was an error, so deduction fails. */ subst_fail(*copy_error); } /* if */ /* Here, either expr_copy is non-NULL and points to the expression copy, or expr_copy is NULL and *constant has been set to the constant result after substitution, or *copy_error is set. *alloc_con is always NULL, because no allocated copy of the constant is available. */ goto end_of_routine; } /* if */ switch (expr->kind) { case enk_constant: /* The expression is just a constant. Do substitution and return the constant after substitution. */ *alloc_con = copy_template_param_con(node_constant(expr), template_arg_list, template_param_list, guide_type, source_pos, options, copy_error, ctws_state, constant); break; case enk_operation: op = expr->variant.operation.kind; if (op == (an_expr_operator_kind)eok_lvalue) { /* Copy the operand of an eok_lvalue node as an lvalue. */ an_expr_node_ptr operand_1 = expr->variant.operation.operands; expr_copy = copy_template_param_expr_as_lvalue(operand_1, template_arg_list, template_param_list, guide_type, source_pos, options, copy_error, ctws_state); } else if (!operator_is_foldable(expr)) { /* For operators we can't ever fold (e.g., calls), give up on deduction. Note that this implements old rules for deduction. When cpp11_sfinae_enabled is TRUE, a broader range of operators are rescanned at the expression level instead of here. */ subst_fail(*copy_error); } else { an_expr_node_ptr operand_1 = expr->variant.operation.operands; an_expr_node_ptr operand_2 = operand_1->next; an_expr_node_ptr operand_3 = NULL; an_expr_node_ptr new_operand_1, new_operand_2 = NULL; an_expr_node_ptr new_operand_3 = NULL; a_type_ptr new_op1_type = NULL; a_type_ptr new_op2_type = NULL; a_type_ptr new_op3_type = NULL; a_constant_ptr alloc_con_1, alloc_con_2 = NULL, alloc_con_3 = NULL; a_boolean folded_to_constant = FALSE; a_type_ptr operation_type = expr->type; a_type_ptr local_guide_type = NULL; if (op == (an_expr_operator_kind)eok_cast) { /* Determine the result type of a cast by substitution. This is done early so we have it as a guide type in case the first operand needs one (e.g., if it's an overloaded function). */ local_guide_type = copy_type_with_substitution(operation_type, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state); if (*copy_error) break; } /* if */ /* Do substitution on the operands. */ new_operand_1 = copy_template_param_expr(operand_1, template_arg_list, template_param_list, local_guide_type, source_pos, options, copy_error, ctws_state, constant_1, &alloc_con_1); if (!*copy_error) { new_op1_type = type_of_copied_template_expr(new_operand_1, constant_1, alloc_con_1); } /* if */ if (operand_2 != NULL) { new_operand_2 = copy_template_param_expr(operand_2, template_arg_list, template_param_list, (a_type_ptr)NULL, source_pos, options, copy_error, ctws_state, constant_2, &alloc_con_2); if (!*copy_error) { new_op2_type = type_of_copied_template_expr(new_operand_2, constant_2, alloc_con_2); } /* if */ operand_3 = operand_2->next; if (operand_3 != NULL) { new_operand_3 = copy_template_param_expr(operand_3, template_arg_list, template_param_list, (a_type_ptr)NULL, source_pos, options, copy_error, ctws_state, constant_3, &alloc_con_3); if (!*copy_error) { new_op3_type = type_of_copied_template_expr(new_operand_3, constant_3, alloc_con_3); } /* if */ } /* if */ } /* if */ if ((new_op1_type != NULL && is_nullptr_type(new_op1_type) && op != (an_expr_operator_kind)eok_question) || (new_op2_type != NULL && is_nullptr_type(new_op2_type)) || (new_op3_type != NULL && is_nullptr_type(new_op3_type))) { /* Only a small subset of possible operations are permitted on operands with a nullptr type. If substitution has resulted in an expression involving such operands, check to make sure it is one of the permitted ones; if not, substitution fails. */ a_constant_ptr op1_con = new_operand_1 != NULL ? NULL : alloc_con_1 != NULL ? alloc_con_1 : constant_1; a_constant_ptr op2_con = new_operand_2 != NULL ? NULL : alloc_con_2 != NULL ? alloc_con_2 : constant_2; a_constant_ptr op3_con = new_operand_3 != NULL ? NULL : alloc_con_3 != NULL ? alloc_con_3 : constant_3; check_template_nullptr_operation(op, new_op1_type, op1_con, new_op2_type, op2_con, new_op3_type, op3_con, copy_error); } /* if */ if (*copy_error) break; /* Do the usual arithmetic conversion or the like on the operands after substitution. */ do_conversions_on_operands_of_copied_template_expr( op, &new_operand_1, constant_1, &alloc_con_1, operand_2 != NULL, &new_operand_2, constant_2, &alloc_con_2, operand_3 != NULL, &new_operand_3, constant_3, &alloc_con_3, source_pos, &operation_type, copy_error); if (*copy_error) break; if (op == (an_expr_operator_kind)eok_cast) { /* The result type for a cast was determined above. */ operation_type = local_guide_type; } else if (op == (an_expr_operator_kind)eok_parens) { /* The result type of a parenthesis operator is its (substituted) operand type. */ operation_type = type_of_copied_template_expr(new_operand_1, alloc_con_1, constant_1); } /* if */ if (new_operand_1 == NULL && new_operand_2 == NULL && new_operand_3 == NULL) { /* All the operands are constant. */ if (alloc_con_1 != NULL) copy_constant(alloc_con_1, constant_1); if (alloc_con_2 != NULL) copy_constant(alloc_con_2, constant_2); if (alloc_con_3 != NULL) copy_constant(alloc_con_3, constant_3); /* Do not fold if any of the constants is still a template parameter constant. */ if ((constant_1->kind != (a_constant_repr_kind)ck_template_param || op == (an_expr_operator_kind)eok_cast) && (operand_2 == NULL || constant_2->kind != (a_constant_repr_kind)ck_template_param) && (operand_3 == NULL || constant_3->kind != (a_constant_repr_kind)ck_template_param)) { /* All the operands are constants and not template parameter constants. Fold the operation. */ a_boolean did_not_fold, template_constant; expr_copy = NULL; folded_to_constant = TRUE; if (operand_2 != NULL) { if (operand_3 != NULL) { check_assertion(op == (an_expr_operator_kind)eok_question); /* Three-operand operation, "?". */ if (is_false_constant(constant_1)) { /* Operand 1 is false, so the result is operand 3. */ *alloc_con = alloc_con_3; if (alloc_con_3 == NULL) *constant = *constant_3; } else { /* Operand 1 is true, so the result is operand 2. */ *alloc_con = alloc_con_2; if (alloc_con_2 == NULL) *constant = *constant_2; } /* if */ } else { /* Two-operand operation. */ binary_operation(op, constant_1, constant_2, operation_type, constant, /*constant_context=*/TRUE, /*evaluated_context=*/TRUE, &did_not_fold, &template_constant, &error_detected, source_pos); check_assertion(!did_not_fold); *alloc_con = NULL; if (error_detected != ec_no_error) subst_fail(*copy_error); } /* if */ } else if (op == (an_expr_operator_kind)eok_cast) { a_boolean is_implicit_cast = expr->compiler_generated; a_boolean is_template_arg, is_reinterpret_cast; is_template_arg = (options & CTWS_NONTYPE_TEMPLATE_ARG) != 0; if (is_template_arg) options &= ~CTWS_NONTYPE_TEMPLATE_ARG; if (!substituted_cast_is_valid(constant_1, operation_type, !is_implicit_cast, is_template_arg, &is_reinterpret_cast)) { subst_fail(*copy_error); } else { is_reinterpret_cast = expr->variant.operation.is_reinterpret_cast; copy_constant(constant_1, constant); type_change_constant_full(constant, operation_type, is_implicit_cast, /*constant_context=*/TRUE, /*evaluated_context=*/TRUE, /*fold_constant_addr_exprs=*/TRUE, /*is_cli_attr_arg_expression=*/FALSE, /*check_cast_access=*/FALSE, /*check_ambiguity=*/TRUE, is_reinterpret_cast, /*maintain_expression=*/FALSE, &did_not_fold, &error_detected, source_pos); check_assertion(!did_not_fold); *alloc_con = NULL; if (error_detected != ec_no_error) subst_fail(*copy_error); } /* if */ } else if (op == (an_expr_operator_kind)eok_parens) { copy_constant(constant_1, constant); *alloc_con = NULL; } else { /* One-operand operation. */ unary_operation(op, constant_1, operation_type, constant, /*constant_context=*/TRUE, /*evaluated_context=*/TRUE, &did_not_fold, &template_constant, &error_detected, source_pos); check_assertion(!did_not_fold); *alloc_con = NULL; if (error_detected != ec_no_error) subst_fail(*copy_error); } /* if */ } /* if */ } else if (op == (an_expr_operator_kind)eok_address_of) { /* Taking the address of a constant-address lvalue produces a constant. */ if (new_operand_1 != NULL && is_glvalue_node(new_operand_1) && constant_glvalue_address(new_operand_1, constant, /*address_escapes=*/TRUE)) { folded_to_constant = TRUE; expr_copy = NULL; *alloc_con = NULL; } /* if */ } /* if */ if (!folded_to_constant) { /* Some operand is non-constant or a template parameter, so an expression is needed. */ /* Allocate the node for each operand if it has not been allocated yet. */ new_operand_1 = alloc_copied_template_param_expr( new_operand_1, constant_1, alloc_con_1); if (operand_2 != NULL) { new_operand_2 = alloc_copied_template_param_expr( new_operand_2, constant_2, alloc_con_2); if (operand_3 != NULL) { new_operand_3 = alloc_copied_template_param_expr( new_operand_3, constant_3, alloc_con_3); } /* if */ } /* if */ /* Link the operand expressions together and create a new expression node. */ new_operand_1->next = new_operand_2; if (new_operand_2 != NULL) { new_operand_2->next = new_operand_3; } /* if */ /* Note that the copy and operator setting here are done in a way that preserves any incidental flags set on the operation, e.g., compiler_generated. */ expr_copy = copy_node(expr); set_node_operator(expr_copy, op, operation_type, expr->is_lvalue, new_operand_1); copy_node_value_category(expr, expr_copy); } /* if */ } /* if */ break; case enk_variable: case enk_routine: { a_boolean folded_to_constant = FALSE; if (guide_type != NULL && is_ptr_or_ref_type(guide_type) && constant_glvalue_address(expr, constant, /*address_escapes=*/TRUE)) { /* See if the resulting constant can bind to the destination parameter. (In cases like this, the guide type is always due to a cast placed to match a nontype template argument to a template parameter of unknown type.) */ a_type_ptr src_type = expr->type, dst_type = guide_type; a_std_conv_descr std_conv; a_boolean cast_to_ref = is_any_reference_type(guide_type); if (!cast_to_ref) { src_type = do_implicit_type_transformations(src_type, (an_operand*)NULL); constant->type = src_type; } else if (expr->is_lvalue) { /* Re-frame the conversion in terms of a pointer cast. */ constant->type = make_reference_type(src_type); src_type = make_pointer_type(src_type); dst_type = make_pointer_type(type_pointed_to(dst_type)); } /* if */ if (impl_conversion_possible( src_type, /*source_is_constant=*/TRUE, /*source_is_string_literal=*/FALSE, is_routine_node(expr), /*is_copy_initialization=*/FALSE, constant, dst_type, /*singleton_braced_init=*/FALSE, /*allow_qualifier_or_eh_mismatch=*/FALSE, /*suppress_extensions=*/TRUE, ec_no_error, &std_conv) && conversion_allowed_for_nontype_template_argument( &std_conv, src_type, /*source_is_constant=*/TRUE, constant, dst_type, (an_error_code *)NULL)) { folded_to_constant = TRUE; } /* if */ } /* if */ if (!folded_to_constant) { expr_copy = copy_expr_tree(expr, CE_NO_OPTIONS); if (expr_copy->is_lvalue && is_variable_node(expr_copy) && guide_type != NULL && !is_any_reference_type(guide_type)) { /* expr is an lvalue variable access, but it has a cast to a nonreference type on top of it. This can happen when the cast represents a nontype template parameter binding where the type of the parameter is itself template dependent (see scan_template_argument_constant_expression). In that case, fold the variable access if possible. */ a_variable_ptr vp = node_variable(expr_copy); a_constant_ptr cp = var_constant_value(vp); if (cp != NULL) { copy_constant(cp, constant); constant->expr = NULL; folded_to_constant = TRUE; } /* if */ } /* if */ } /* if */ if (folded_to_constant) { expr_copy = NULL; *alloc_con = NULL; } /* if */ } /* if */ break; case enk_temp_init: /* A functional-notation cast with empty parentheses or braces, e.g., T(), can become a constant here if T is now a scalar type (or, in C++11, literal type). Expression rescanning handles fancier cases, but this is all we need here for the old-style SFINAE handling. */ { a_dynamic_init_ptr dip = expr->variant.init.dynamic_init; if (dip->kind == (a_dynamic_init_kind)dik_zero) { a_type_ptr new_type = copy_type_with_substitution( expr->type, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state); if (!*copy_error && !is_bad_type_for_template_arg_operand(new_type) && !is_template_param_type(new_type)) { make_zero_of_proper_type(new_type, constant); expr_copy = NULL; goto end_of_routine; } /* if */ } else if (dip->kind == (a_dynamic_init_kind)dik_constant) { /* Even with new-style SFINAE we may end up here with a temporary initialized with a constant (e.g., a cast to an rvalue reference can create a temporary initialized with a constant; other cases are also possible). Use the underlying constant. */ *alloc_con = copy_template_param_con(dip->variant.constant.ptr, template_arg_list, template_param_list, guide_type, source_pos, options, copy_error, ctws_state, constant); goto end_of_routine; } /* if */ } subst_fail(*copy_error); break; case enk_reuse_value: /* This might come up because of the GNU two-operand "?". If it does, just make sure we don't abort. */ subst_fail(*copy_error); break; case enk_type_operand: { a_type_ptr new_type = copy_type_with_substitution( expr->variant.type_operand.type, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state); if (!*copy_error) { expr_copy = copy_node(expr); expr_copy->variant.type_operand.type = new_type; } /* if */ } break; case enk_builtin_operation: expr_copy = copy_template_param_builtin_operation( expr, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state, constant); break; case enk_param_ref: /* A reference to a parameter. Copy the expression node, but apply substitutions to its type. */ { a_type_ptr new_type = copy_type_with_substitution( expr->type, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state); if (!*copy_error) { expr_copy = copy_node(expr); expr_copy->type = new_type; } /* if */ } break; case enk_braced_init_list: { an_expr_node_ptr list; list = copy_template_param_expr_list(expr->variant.braced_init_list, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state); if (!*copy_error) { expr_copy = copy_node(expr); expr_copy->variant.braced_init_list = list; } /* if */ } break; case enk_pack_index: { an_expr_node_ptr new_pack_expr = NULL, new_index_expr = NULL; a_constant_ptr alloc_con_1 = NULL, alloc_con_2 = NULL; new_pack_expr = copy_template_param_expr( expr->variant.pack_index.expr, template_arg_list, template_param_list, (a_type_ptr)NULL, source_pos, options, copy_error, ctws_state, constant_1, &alloc_con_1); if (!*copy_error) { new_index_expr = copy_template_param_expr( expr->variant.pack_index.index_expr, template_arg_list, template_param_list, (a_type_ptr)NULL, source_pos, options, copy_error, ctws_state, constant_2, &alloc_con_2); } /* if */ if (!*copy_error) { /* Ensure both children are represented as expression nodes. */ new_pack_expr = alloc_copied_template_param_expr(new_pack_expr, constant_1, alloc_con_1); new_index_expr = alloc_copied_template_param_expr(new_index_expr, constant_2, alloc_con_2); expr_copy = copy_node(expr); expr_copy->variant.pack_index.expr = new_pack_expr; expr_copy->variant.pack_index.index_expr = new_index_expr; } /* if */ } break; case enk_concept_id: { a_template_arg_ptr new_args = copy_template_arg_list_with_substitution_rebuilding_arg_operands( (a_symbol_ptr)NULL, expr->variant.concept_id.args, (a_template_param_ptr)NULL, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state); if ((options & CTWS_MAY_BE_RESCANNED) != 0 || new_args == NULL || template_arg_list_is_dependent(new_args)) { /* Don't attempt to evaluate concept-ids that will be rescanned or that have dependent parameter lists. */ if (!*copy_error) { expr_copy = copy_node(expr); expr_copy->variant.concept_id.args = new_args; } /* if */ } else { a_boolean val; a_subst_pairs_array subst_pairs(1); subst_pairs.push_back({ template_param_list, template_arg_list, FALSE, FALSE, FALSE, FALSE }); val = is_substituted_concept_satisfied(expr, subst_pairs, (a_diag_list_ptr)NULL, CTWS_NO_OPTIONS, ctws_state, copy_error); make_bool_constant_value(val, constant); free_template_arg_list(new_args); } /* if */ } break; case enk_requires: if ((options & CTWS_MAY_BE_RESCANNED) || template_arg_list_is_dependent(template_arg_list)) { /* Don't attempt to substitute requires-expressions with dependent parameter lists. Exceptions occur when we are adjusting template parameter coordinates or synthesizing an alias deduction guide: In that case we store the template arguments for later substitution. */ if (options & (CTWS_MAY_BE_RESCANNED | CTWS_ADJUST_COORDINATES | CTWS_ALIAS_DEDUCTION_GUIDE)) { a_subst_pairs_array subst_pairs = requires_expr_substs->get(expr); a_boolean adjust_coordinates = (options & CTWS_ADJUST_COORDINATES) != 0; a_boolean is_alias_deduction_guide = (options & CTWS_ALIAS_DEDUCTION_GUIDE) != 0; expr_copy = copy_node(expr); subst_pairs.insert(0, a_subst_pairs_descr{ template_param_list, copy_template_arg_list(template_arg_list), TRUE, FALSE, (a_bit_field)adjust_coordinates, (a_bit_field)is_alias_deduction_guide }); requires_expr_substs->map_or_replace(expr_copy, subst_pairs); } else { subst_fail(*copy_error); } /* if */ } else { /* Get any delayed substitution pairs. */ a_subst_pairs_array subst_pairs = requires_expr_substs->get(expr); a_boolean val; if (subst_pairs.is_empty() || subst_pairs.front_elem().args_known_nondependent) { /* If the existing substitution pairs are for non-dependent arguments of enclosing scopes, we need to add the current (innermost) substitution pair to the end. */ subst_pairs.push_back(a_subst_pairs_descr{ template_param_list, template_arg_list, FALSE, TRUE, FALSE, FALSE }); } else { subst_pairs.insert(0, a_subst_pairs_descr{ template_param_list, template_arg_list, FALSE, TRUE, FALSE, FALSE }); } /* if */ val = requires_expr_satisfied_full(expr, subst_pairs, ctws_state); make_bool_constant_value(val, constant); } /* if */ break; case enk_lambda: expr_copy = copy_template_param_lambda_expr(expr, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state); break; case enk_error: subst_fail(*copy_error); break; default: /* Other kinds of expressions cannot come up in this context. */ check_assertion_str(non_constant_expr, "copy_template_param_expr: bad expression kind"); subst_fail(*copy_error); break; } /* switch */ end_of_routine: if (*copy_error) { /* Return an error node on a copy error. */ expr_copy = error_node(); *alloc_con = NULL; } /* if */ release_local_constant(&constant_1); release_local_constant(&constant_2); release_local_constant(&constant_3); return expr_copy; } /* copy_template_param_expr */ an_expr_node_ptr copy_expr_with_substitutions( an_expr_node_ptr expr, a_template_arg_ptr template_arg_list, a_template_param_ptr template_param_list, a_ctws_options_set options, a_boolean *copy_error, a_ctws_state_ptr ctws_state) /* Create and return a copy of the given expression, with the given template parameters substituted by the given template argument list. If this process fails, set *copy_error to TRUE. The substitution process is guided by the given options and the given state object. See also substitute_expr, which can substitute multiple levels of template parameters. */ { a_constant_ptr const_result = local_constant(); a_constant_ptr alloc_const_result; expr = copy_template_param_expr(expr, template_arg_list, template_param_list, /*guide_type=*/expr->type, &expr->position, options, copy_error, ctws_state, const_result, &alloc_const_result); if (*copy_error) { expr = NULL; } else { expr = alloc_copied_template_param_expr(expr, const_result, alloc_const_result); } /* if */ release_local_constant(&const_result); return expr; } /* copy_expr_with_substitutions */ a_type_ptr type_of_decltype_expr_with_substitution( a_type_ptr type, an_expr_node_ptr expr, a_template_arg_ptr template_arg_list, a_template_param_ptr template_param_list, a_ctws_options_set options, a_boolean *copy_error, a_ctws_state_ptr ctws_state) /* type is a decltype type, and expr is the expression that underlies it. Make a copy of the expression, doing template substitution according to template_arg_list, template_param_list, and options, with an associated source position of source_pos. Return the decltype type after substitution, or *copy_error set to TRUE for an error. Also used for typeof cases; "type" can be consulted to tell the difference. */ { a_type_ptr new_type = NULL; if (cpp11_sfinae_enabled) { /* C++11 SFINAE rules: do the substitution by rescanning. */ new_type = decltype_of_expr_with_substitution(type, expr, template_arg_list, template_param_list, options, copy_error, ctws_state); } else { /* Pre-C++11 SFINAE rules apply, so deduction fails. */ subst_fail(*copy_error); } /* if */ return new_type; } /* type_of_decltype_expr_with_substitution */ a_symbol_ptr symbol_for_template_param_unknown_entity_con_after_substitution( a_constant_ptr con, a_template_arg_ptr template_arg_list, a_template_param_ptr template_param_list, a_source_position *source_pos, a_ctws_state_ptr ctws_state, a_ctws_options_set options) /* con is a ck_template_param constant for an unknown template-dependent entity. Conceptually replace any occurrences of template parameters with the corresponding values from the template argument list template_arg_list, and return a pointer to the symbol for the entity selected after substitution, or NULL if there is no such symbol or if there is a substitution error. template_param_list is the parameter list for which template_arg_list is an argument list. source_pos give the source position. options is a set of options for the copy/substitution. */ { a_symbol_ptr sym = NULL; a_type_ptr parent_type; a_boolean copy_error = FALSE; check_assertion(con->kind == (a_constant_repr_kind)ck_template_param && (con->variant.template_param.kind == (a_template_param_constant_kind)tpck_member || con->variant.template_param.kind == (a_template_param_constant_kind)tpck_destructor || con->variant.template_param.kind == (a_template_param_constant_kind)tpck_unknown_function)); if (!con->source_corresp.is_class_member) { check_assertion(con->variant.template_param.kind == (a_template_param_constant_kind)tpck_unknown_function); /* For a non-member unknown function, the original symbol (probably an overload set) was saved when this constant was created. */ sym = con->variant.template_param.variant.unknown_function.symbol; if (sym == NULL) { expect_error(); } else if (microsoft_mode && !do_dependent_name_processing) { /* In Microsoft mode, the lookup done previously might not have found some instances of the function declared after the template. Do the lookup again to get the full overload set. */ a_symbol_locator locator; a_symbol_ptr new_sym; make_locator_for_symbol(sym, &locator); locator.specific_symbol = NULL; if (!symbol_is(sym, sk_namespace_projection) && con->source_corresp.parent_scope != NULL && scope_is(con->source_corresp.parent_scope, sck_namespace)) { a_namespace_ptr ns_ptr; ns_ptr = con->source_corresp.parent_scope->variant.assoc_namespace; new_sym = namespace_qualified_id_lookup(&locator, ns_ptr, IDL_NO_OPTIONS); } else { new_sym = normal_id_lookup(&locator, IDL_NO_OPTIONS); } /* if */ if (new_sym != NULL) sym = new_sym; } /* if */ } else { /* Member constant (normal case). */ /* This occurs for member constants specified in forms such as A::x. Do substitution on the parent type and then look up the name in the updated class to see what the member is. */ a_symbol_ptr orig_sym = symbol_for(con); check_assertion(orig_sym != NULL); /* For a tpck_unknown_function constant with an underlying symbol, use that symbol for the substitution. */ if (is_unknown_function_constant(con)) { a_symbol_ptr under_sym = con->variant.template_param .variant.unknown_function.symbol; if (under_sym != NULL) orig_sym = under_sym; } /* if */ if (con->source_corresp.parent_scope == NULL) { /* In some error situations the parent scope may be NULL, but the parent type might have been a nonreal class type with no associated scope. */ copy_error = TRUE; goto done; } /* if */ parent_type = parent_class_of(con); if (parent_type->source_corresp.member_of_unknown_base) { /* We're pretending that we found the member in a dependent base class. That means the original form of reference was unqualified. Leave the constant as it is. */ sym = orig_sym; } else { /* We don't want to inherit the parent_levels setting from the ctws_state passed in. */ a_ctws_state local_ctws_state; local_ctws_state = *ctws_state; local_ctws_state.parent_levels = 0; sym = copy_parent_type_with_substitution(orig_sym, parent_type, template_arg_list, template_param_list, source_pos, /*is_type=*/FALSE, NULL, options, ©_error, &local_ctws_state); if (sym == orig_sym) { /* A reference like "X::operator T" will not be substituted by the call above because the parent type is not altered. Check for an unknown conversion function that must be processed. */ a_type_ptr conv_type; conv_type = type_if_unknown_conversion_function_symbol(orig_sym); if (conv_type != NULL) { /* Substitute any template parameters in the conversion type. */ conv_type = copy_type_with_substitution(conv_type, template_arg_list, template_param_list, source_pos, options, ©_error, ctws_state); /* Look for a conversion function that converts to the new type. */ sym = look_up_conversion_function(parent_type, conv_type, source_pos); } /* if */ } /* if */ } /* if */ } /* if */ done: if (copy_error) sym = NULL; return sym; } /* symbol_for_template_param_unknown_entity_con_after_substitution */ a_constant_ptr strip_implicit_casts_if_template_param_constant( a_constant_ptr constant) /* If constant is a template parameter constant (i.e., a ck_template_param), check for the case of an underlying template parameter constant to which implicit cast(s) have been added. If this is such a case, return the underlying template parameter constant, otherwise return the original constant. */ { a_constant_ptr result = constant; /* Remove an implicit eok_cast. */ if (constant_is(constant, ck_template_param) && tpck_is(constant, tpck_expression)) { an_expr_node_ptr expr = expr_node_from_tpck_expression(constant); if (is_operation_node(expr) && (node_operator_is(expr, eok_cast) || node_operator_is(expr, eok_lvalue)) && expr->compiler_generated) { an_expr_node_ptr operand = expr->variant.operation.operands; if (is_constant_node(operand)) { constant = node_constant(operand); } /* if */ } /* if */ } /* if */ /* If, after possibly removing the casts above, the underlying constant is a template parameter constant, use that constant. */ if (constant_is(constant, ck_template_param)) { result = constant; } /* if */ return result; } /* strip_implicit_casts_if_template_param_constant */ static a_constant_ptr copy_template_param_unknown_entity_con( a_constant_ptr con, a_template_arg_ptr template_arg_list, a_template_param_ptr template_param_list, a_type_ptr guide_type, a_boolean is_address, a_boolean is_template_ref, a_template_arg_ptr ref_arg_list, a_source_position *source_pos, a_ctws_state_ptr ctws_state, a_ctws_options_set options, a_boolean *copy_error, a_constant_ptr constant) /* Copy the ck_template_param/tpck_member or .../tpck_unknown_function constant "con", replacing any occurrences of template parameters with the corresponding values from the template argument list template_arg_list, and return a pointer to the copy after substitution. template_param_list is the parameter list for which template_arg_list is an argument list. If there is no allocated instance of the constant, set *constant to the constant value and return NULL. If guide_type is non-NULL, it is a "guide" type for the constant, either the type of the template parameter or the destination type of a cast above this constant. is_address is TRUE if the constant produced should be for the address of the member rather than the value (e.g., there is a tpck_address constant over this constant). is_template_ref is TRUE if the constant produced should be for a function template followed by explicit arguments (i.e., there is a tpck_template_ref over this constant); ref_arg_list provides the explicit argument list in that case. source_pos provides the source position for any calls of copy_type_with_substitution. If there is an error in the copying (specifically, if there is an error in doing substitution on a type), set *copy_error to TRUE. options is a set of name lookup options. */ { a_symbol_ptr sym; a_boolean err = FALSE, type_check_needed = (guide_type != NULL); a_boolean unhandled_template_args = is_template_ref; a_constant_ptr con_copy = con; /* Extract the symbol for the entity after substitution. */ sym = symbol_for_template_param_unknown_entity_con_after_substitution( con, template_arg_list, template_param_list, source_pos, ctws_state, options); if (sym != NULL) sym = fundamental_symbol_of(sym); if (sym == NULL) { /* The substituted parent class has no member of the specified name. */ err = TRUE; } else if (sym->kind == (a_symbol_kind)sk_constant) { con_copy = sym->variant.constant; } else if (sym->kind == (a_symbol_kind)sk_static_data_member) { a_variable_ptr var = sym->variant.static_data_member.variable; if (!is_address) { /* The value of a static data member is acceptable as a result if the data member can be used as a constant. */ con_copy = var_constant_value_full(var, /*copy_for_reuse=*/TRUE, /*clear_backing_expr=*/TRUE, /*allow_C_mode_const_var=*/FALSE); if (con_copy == NULL) err = TRUE; } else { /* Address of a static data member. */ set_variable_address_constant(var, constant, /*set_address_taken_flag=*/TRUE); con_copy = NULL; } /* if */ } else if (sym->kind == (a_symbol_kind)sk_field && is_address) { /* The address of a nonstatic data member: pointer to member. */ a_field_ptr field = sym->variant.field.ptr; set_ptr_to_data_member_constant(field, constant); con_copy = NULL; } else if ((sym->kind == (a_symbol_kind)sk_member_function || sym->kind == (a_symbol_kind)sk_function_template || sym->kind == (a_symbol_kind)sk_overloaded_function) && guide_type != NULL && (is_address || is_pointer_type(guide_type))) { /* A member function is acceptable as a pointer or pointer-to-member. Choose a function from the overload set based on the guide type. */ if (is_template_dependent_type(guide_type) || template_arg_list_is_dependent(ref_arg_list)) { /* The guide type is dependent or the template argument list being used still contains template parameters, so we can't choose a particular function yet. Build a constant to represent the unresolved function. */ a_constant_ptr fcon; a_symbol_ptr unk_sym; check_assertion(con->kind == (a_constant_repr_kind)ck_template_param); unk_sym = find_unknown_function_symbol( sym, con->variant.template_param.is_qualified_name); check_assertion(unk_sym->kind == (a_symbol_kind)sk_constant); fcon = unk_sym->variant.constant; if (is_template_ref) { /* Add a tpck_template_ref for the template arguments. */ a_constant_ptr tcon = local_constant(); clear_constant(tcon, (a_constant_repr_kind)ck_template_param); set_template_param_constant_kind(tcon, (a_template_param_constant_kind)tpck_template_ref); tcon->variant.template_param.variant.template_ref.con = fcon; tcon->variant.template_param.variant.template_ref.arg_list = ref_arg_list; tcon->type = type_of_unknown_templ_param_nontype; fcon = alloc_shareable_constant(tcon); unhandled_template_args = FALSE; release_local_constant(&tcon); } /* if */ /* Add a substitutable cast to the guide type. */ make_template_param_cast_constant(fcon, constant, guide_type, /*is_explicit=*/FALSE); con_copy = NULL; } else { choose_function_and_make_address_constant(sym, is_template_ref, ref_arg_list, guide_type, constant, &err); unhandled_template_args = FALSE; con_copy = NULL; } /* if */ type_check_needed = FALSE; } else { err = TRUE; } /* if */ if (!err && type_check_needed) { /* The type of the entity must match the guide type. If it doesn't, deduction fails. Some kinds of conversions are allowed. */ a_constant_ptr source_con = (con_copy != NULL) ? con_copy : constant; a_type_ptr source_type = source_con->type; if (!identical_types(guide_type, source_type)) { a_std_conv_descr std_conv; if (!impl_conversion_possible(source_type, /*source_is_constant=*/TRUE, /*source_is_string_literal=*/FALSE, /*source_is_function=*/FALSE, /*is_copy_initialization=*/FALSE, source_con, guide_type, /*singleton_braced_init=*/FALSE, /*allow_qualifier_or_eh_mismatch=*/FALSE, /*suppress_extensions=*/TRUE, ec_no_error, &std_conv) || !conversion_allowed_for_nontype_template_argument( &std_conv, source_type, /*source_is_constant=*/TRUE, source_con, guide_type, (an_error_code *)NULL)) { err = TRUE; } /* if */ } /* if */ } /* if */ if (unhandled_template_args) err = TRUE; if (err) { /* The constant was specified as something like A::B, but the substituted "A" does not contain a B, or the B found is not a constant. */ subst_fail(*copy_error); con_copy = alloc_error_constant(); } /* if */ return con_copy; } /* copy_template_param_unknown_entity_con */ a_targ_alignment compute_alignof_value(a_type_ptr alignof_type, a_boolean is_type, an_expr_node_ptr expr, a_source_position *diag_pos, a_boolean *p_is_error, a_boolean *p_template_case) /* Compute and return the result of "alignof" (or a variant, like "__alignof" or "__ALIGNOF__") applied to the given type (or, if is_type is FALSE, to an expression of that type; for cases where the operand is represented by an expression node, expr is that node). In error cases set *p_is_error to TRUE and, if appropriate and diag_pos is non-NULL, issue the diagnostic at the given position. (The return value is more or less arbitrary in error cases.) If the alignof operand is instantiation dependent, set *p_template_case to TRUE. */ { a_targ_alignment alignof_value = 0; a_boolean is_error = FALSE, template_case = FALSE; /* Instantiate the type if it is a template class. */ complete_type_is_needed(alignof_type); if (is_error_type(alignof_type)) { is_error = TRUE; } else if (!C_mode() && is_template_dependent_context() && is_template_dependent_type(alignof_type)) { template_case = TRUE; } else { if (expr != NULL && is_operation_node(expr)) { /* Field selection operations need special treatment: The alignment of the field (including field-specific attributes) is produced. */ an_expr_operator_kind opkind = expr->variant.operation.kind; if (opkind == (an_expr_operator_kind)eok_dot_field || opkind == (an_expr_operator_kind)eok_points_to_field) { an_expr_node_ptr field_op = expr->variant.operation.operands->next; alignof_value = alignment_of_field_full( node_field(field_op), /*for_alignof=*/TRUE); } /* if */ } /* if */ if (!(gnu_mode && gnu_version < 30100) && expr != NULL && expr->is_lvalue && is_variable_node(expr)) { /* If the expression is an lvalue for a variable with an explicit alignment, use it. (GNU C++ versions prior to 3.1 ignore the explicit alignment.) */ a_variable_ptr var = node_variable(expr); if (var->alignment != 0) { alignof_value = var->alignment; } /* if */ } /* if */ if (is_incomplete_type(skip_array_types(alignof_type))) { an_error_severity severity; if ((gnu_mode && is_type && !is_void_type(alignof_type)) || strict_ansi_mode) { /* Issue an error in strict ANSI mode. GNU compilers issue an error if the argument is not an expression and is not a void type. */ severity = (an_error_severity)es_error; is_error = TRUE; } else { severity = (an_error_severity)es_warning; } /* if */ if (diag_pos != NULL) { expr_pos_diagnostic(severity, ec_alignof_incomplete_type, diag_pos); } /* if */ } else if (is_sizeless_type(skip_array_types(alignof_type))) { is_error = TRUE; if (diag_pos != NULL) { expr_pos_ty_diagnostic(es_error, ec_sizeless_type_not_allowed, diag_pos, alignof_type); } /* if */ } /* if */ /* Force building a template-dependent representation for cases that involve a dependent expression even though the result type is not dependent. This is done after the type validity tests above so we can detect any possible errors anyway. */ if (is_template_dependent_context() && (expr != NULL ? expr_is_instantiation_dependent(expr) : is_instantiation_dependent_type(alignof_type))) { template_case = TRUE; } /* if */ } /* if */ if (!is_error && !template_case && alignof_value == 0) { alignof_value = alignment_of_type(alignof_type); #if MICROSOFT_EXTENSIONS_ALLOWED if (microsoft_mode && is_incomplete_type(alignof_type)) { /* In Microsoft mode, the alignment-of operator sometimes returns zero for incomplete types. */ if (is_array_type(alignof_type)) { /* Microsoft ignores array declarators to determine alignment. This matters particularly for arrays of unspecified length: __alignof(int[]) is the same as __alignof(int). */ alignof_type = underlying_array_element_type(alignof_type); alignof_value = alignment_of_type(alignof_type); } /* if */ if (is_class_struct_union_type(alignof_type) || (C_mode() && is_void_type(alignof_type))) { alignof_value = 0; } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ *p_template_case = template_case; *p_is_error = is_error; return alignof_value; } /* compute_alignof_value */ #endif /* !STANDALONE_UTILITY_PROGRAM */ #if IA64_ABI a_targ_size_t compute_dsize(a_type_ptr class_type) /* The IA-64 ABI layout algorithm uses "dsize" to represent the size of a class prior to rounding up for alignment purposes. That value is computed during class layout but is not stored in the IL. When laying out potentially- overlapping data members, the "dsize" value for the member is necessary and is computed here (by adding the offset of the last field or virtual base of the class to the size of that field or base). */ { a_field_ptr fp; a_base_class_ptr bcp; a_targ_size_t result = 0, field_size; a_type_ptr fp_type; check_assertion(is_immediate_class_type(class_type)); for (fp = class_type->variant.class_struct_union.field_list; fp != NULL && fp->next != NULL; fp = fp->next) { } /* for */ if (fp == NULL) { /* No fields; see if there are any base classes, and if so find the last one. */ for (bcp = base_classes_of(class_type); bcp != NULL && bcp->next != NULL; bcp = bcp->next) { } /* for */ if (bcp == NULL) { /* No fields or base classes. See if there is a vptr. */ if (needs_virtual_function_table(class_type)) { result = targ_sizeof_pointer; } /* if */ } else { result = bcp->offset + bcp->type->size; } /* if */ } else { fp_type = skip_typerefs(fp->type); if (is_immediate_class_type(fp_type)) { field_size = compute_dsize(fp_type); } else { field_size = fp_type->size; } /* if */ result = fp->offset + field_size; if (class_type->variant.class_struct_union.any_virtual_base_classes) { /* Virtual bases classes can be located beyond the last field. */ for (bcp = base_classes_of(class_type); bcp != NULL; bcp = bcp->next) { if (bcp->is_virtual && !bcp->is_optimized_empty_base && bcp->offset >= result) { result = bcp->offset + class_type_supp(bcp->type) ->size_without_virtual_base_classes; } /* if */ } /* for */ } /* if */ } /* if */ return result; } /* compute_dsize */ #endif /* IA64_ABI */ #if !STANDALONE_UTILITY_PROGRAM static a_constant_ptr copy_template_param_cast_constant( a_constant_ptr con, a_constant_ptr base_con, a_boolean explicit_cast, a_template_arg_ptr template_arg_list, a_template_param_ptr template_param_list, a_source_position *source_pos, a_ctws_options_set options, a_boolean *copy_error, a_ctws_state_ptr ctws_state, a_constant_ptr constant) /* con is a constant for which is_template_param_cast_constant returns TRUE, which means it represents a substitutable cast expression. Perform template substitution on this constant and place the result in *constant, or set *copy_error to TRUE if the substitution fails. base_con is the underlying constant being cast, and explicit_cast indicates whether the cast should be considered an explicit cast (both parameters are produced by a call to is_template_param_cast_constant). See copy_template_param_con for the meaning of the remaining parameters. */ { a_type_ptr new_type, copied_con_type; a_constant_ptr src_con, other_con = NULL, con_copy = con; a_boolean reinterpret_cast_needed = FALSE, is_template_arg; a_ctws_options_set cast_options = CTWS_CAST_OPERAND; new_type = copy_type_with_substitution(con->type, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state); if (*copy_error) goto done; is_template_arg = (options & CTWS_NONTYPE_TEMPLATE_ARG) != 0; if (is_template_arg) options &= ~CTWS_NONTYPE_TEMPLATE_ARG; if (explicit_cast) cast_options |= CTWS_EXPLICIT_CAST_OPERAND; if (base_con->kind == ck_address && base_con->variant.address.kind == abk_routine && base_con->variant.address.variant.routine ->source_corresp.is_class_member) { /* If we have the address of a member function, substitute the class type and look up the member symbol in the substituted class. */ a_symbol_ptr sym = symbol_for(base_con->variant.address.variant.routine); sym = copy_parent_type_with_substitution(sym, sym->parent.class_type, template_arg_list, template_param_list, source_pos, /*is_type=*/FALSE, (a_type_ptr *)NULL, options, copy_error, ctws_state); if (*copy_error) goto done; if (sym != NULL) { if (sym->kind == sk_member_function && sym_parent_class(sym)->variant.class_struct_union.is_nonreal_class) { /* We still have a dependent parent class after substitution. */ other_con = alloc_constant(ck_address); other_con->variant.address.kind = abk_routine; other_con->variant.address.variant.routine = sym->variant.routine.ptr; other_con->type = make_pointer_type(sym->variant.routine.ptr->type); make_template_param_cast_constant(other_con, constant, other_con->type, /*is_explicit=*/FALSE); goto done; } else if (sym->kind == sk_constant) { other_con = sym->variant.constant; } else { other_con = alloc_constant(ck_address); choose_function_and_make_address_constant(sym, /*is_template_id=*/FALSE, (a_template_arg_ptr)NULL, new_type, other_con, copy_error); if (*copy_error) goto done; } /* if */ } /* if */ } /* if */ if (other_con == NULL) { other_con = copy_template_param_con( base_con, template_arg_list, template_param_list, new_type, source_pos, options | cast_options, copy_error, ctws_state, constant); if (*copy_error) goto done; } /* if */ /* Get the type of the copied constant from either other_con or constant, as appropriate. */ src_con = (other_con != NULL) ? other_con : constant; copied_con_type = src_con->type; if (!(options & CTWS_NON_CONSTANT_EXPR) && (is_bad_type_for_template_arg_operand(new_type) || is_bad_type_for_template_arg_operand(copied_con_type)) && !is_valid_templ_arg_constant(src_con) && !types_are_compatible(new_type, copied_con_type)) { /* One of the types is invalid for a template argument constant expression. However, exempt the idiom where a constant is converted to its own type as a way of marking it as dependent. */ subst_fail(*copy_error); } else if (!substituted_cast_is_valid(src_con, new_type, explicit_cast, is_template_arg, &reinterpret_cast_needed)) { /* The cast is not valid. */ subst_fail(*copy_error); } else if (same_entities(new_type, con->type) && other_con == base_con) { /* No change in the type or constant. */ } else { a_boolean did_not_fold; an_error_code error_detected; if (other_con != NULL) *constant = *other_con; /* Do the cast again with the type and constant after substitution. */ type_change_constant_full(constant, new_type, !explicit_cast, /*constant_context=*/TRUE, /*evaluated_context=*/TRUE, /*fold_constant_addr_exprs=*/TRUE, /*is_cli_attr_arg_expression=*/FALSE, /*check_cast_access=*/FALSE, /*check_ambiguity=*/TRUE, reinterpret_cast_needed, /*maintain_expression=*/FALSE, &did_not_fold, &error_detected, source_pos); if (error_detected != ec_no_error || did_not_fold) { subst_fail(*copy_error); } else { con_copy = NULL; } /* if */ } /* if */ done: return con_copy; } /* copy_template_param_cast_constant */ static void copy_constant_for_rescan_if_needed(a_constant_ptr *orig_con, a_constant_ptr caller_con, a_ctws_options_set options) /* This is a helper function for copy_template_param_con. *orig_con is a constant entry that is intended to be returned from copy_template_param_con. However, if options indicates that this is for a substitution result (rescan) that will not itself be substituted, then copy **orig_con to *caller_con except for its backing expression. This is needed because in such cases the backing expression nodes may be reclaimed later on */ { if ((*orig_con)->expr != NULL && (options & CTWS_INSIDE_EXPR_RESCAN) != 0 && (options & (CTWS_PRESERVE_DEDUCED_PACKS | CTWS_PARTIAL_ARG_LIST_OKAY | CTWS_MAY_BE_RESCANNED | CTWS_ADJUST_COORDINATES | CTWS_ALIAS_DEDUCTION_GUIDE)) == 0) { copy_constant(*orig_con, caller_con); caller_con->expr = NULL; *orig_con = NULL; } /* if */ } /* copy_constant_for_rescan_if_needed */ static void substitute_reflection_constant(a_constant *refl_cp, a_template_arg *t_args, a_template_param *t_params, a_ctws_options_set options, a_ctws_state_ptr ctws_state, a_source_position *source_pos, a_boolean *copy_error) /* refl_cp is a reflection value (i.e., refl_cp->kind is ck_reflection). Substitute the given template parameters with the given template arguments in the associated entity (refl_cp->variant.reflection.entity). If successful and the new entity is not instantiation dependent, replace refl_cp->type by the reflection type. If substitution fails, set *copy_error. options, ctws_state, and source_pos are forwarded from copy_template_param_con. */ { a_reflection_value *rvp = &refl_cp->variant.reflection; strip_template_arg(rvp); switch (rvp->entity.kind) { case iek_type: { a_type *type = copy_type_with_substitution((a_type*)rvp->entity.ptr, t_args, t_params, source_pos, options, copy_error, ctws_state); rvp->entity.ptr = (char*)type; if (!is_instantiation_dependent_type(type)) { refl_cp->type = reflection_type(); } /* if */ } break; case iek_constant: { a_constant *old_con = (a_constant*)rvp->entity.ptr, *temp_con = local_constant(), *new_con; new_con = copy_template_param_con(old_con, t_args, t_params, /*guide_type=*/(a_type*)NULL, source_pos, options, copy_error, ctws_state, temp_con); if (!copy_error) { if (new_con == NULL) { new_con = move_local_constant_to_il(&temp_con); } /* if */ rvp->entity.ptr = (char*)new_con; if (!constant_is_instantiation_dependent(new_con)) { refl_cp->type = reflection_type(); } /* if */ } /* if */ if (temp_con != NULL) release_local_constant(&temp_con); } break; case iek_expr_node: { an_expr_node *old_expr = (an_expr_node*)rvp->entity.ptr, *new_expr; a_constant *temp_con = local_constant(); a_rescan_control_block rcblock; clear_rescan_control_block(&rcblock); rcblock.template_arg_list = t_args; rcblock.template_param_list = t_params; rcblock.options = options; rcblock.ctws_state = ctws_state; new_expr = rescan_expr_with_substitution(old_expr, /*guide_type=*/(a_type*)NULL, &rcblock, temp_con); if (rcblock.error_detected) { subst_fail(*copy_error); } else if (new_expr != NULL) { if (!expr_is_instantiation_dependent(new_expr)) { refl_cp->type = reflection_type(); } /* if */ rvp->entity.ptr = (char*)new_expr; } else { if (!constant_is_instantiation_dependent(temp_con)) { refl_cp->type = reflection_type(); } /* if */ rvp->entity.ptr = (char*)move_local_constant_to_il(&temp_con); } /* if */ if (temp_con != NULL) release_local_constant(&temp_con); } break; default: subst_fail(*copy_error); } /* switch */ } /* substitute_reflection_constant */ a_constant_ptr copy_template_param_con( a_constant_ptr con, a_template_arg_ptr template_arg_list, a_template_param_ptr template_param_list, a_type_ptr guide_type, a_source_position *source_pos, a_ctws_options_set options, a_boolean *copy_error, a_ctws_state_ptr ctws_state, a_constant_ptr constant) /* Copy a ck_template_param constant, replacing any occurrences of template parameters with the corresponding values from the template argument list template_arg_list, and return a pointer to the copy after substitution. template_param_list is the parameter list for which template_arg_list is an argument list. If there is no allocated instance of the constant, set *constant to the constant value and return NULL. If guide_type is non-NULL, it is a "guide" type for the constant, either the type of the template parameter or the destination type of a cast above this constant. source_pos provides the source position for any calls of copy_type_with_substitution. If there is an error in the copying (e.g., if there is an error in doing substitution on a type), set *copy_error to TRUE. options is a set of substitution options. */ { a_constant_ptr con_copy, other_con, src_con; a_type_ptr new_type = NULL; a_boolean did_not_fold, reinterpret_cast_needed = FALSE; an_error_code error_detected; a_template_param_coordinate_ptr coordinates; con_copy = con; if (constant_is(con, ck_template_param)) { switch (con->variant.template_param.kind) { case tpck_param: /* The template param constant represents a simple non-type template parameter. Replace it if it has the right depth. */ { a_template_arg_ptr tap; coordinates = &con->variant.template_param.variant.coordinates; tap = get_template_arg_for_coordinates(coordinates, options, &template_arg_list, template_param_list); if (tap == NULL || (is_nontype_templ_arg(tap) && !tap->is_array_bound_of_unknown_type)) { if (tap != NULL && tap->variant.constant != NULL) { /* Only use the template argument value if one was specified. */ Dyn_array *record_used; con_copy = tap->variant.constant; copy_constant_for_rescan_if_needed(&con_copy, constant, options); src_con = con_copy != NULL ? con_copy : constant; if (src_con != NULL && constant_is(src_con, ck_aggregate) && !is_const_qualified_type(src_con->type)) { /* Referring to a template parameter of class type produces a const-qualified result. */ if (con_copy != NULL) copy_constant(con_copy, constant); constant->expr = copy_expr_tree(constant->expr, CE_NO_OPTIONS); constant->type = make_qualified_type(constant->type, TQ_CONST); constant->variant.aggregate.added_const_for_template_param = TRUE; if (con_copy != NULL) con_copy = NULL; } /* if */ record_used = ctws_state->record_used_arguments; if (record_used != NULL) { /* Set a "used" marker for the position of the template argument. */ record_used->resize(max_val((size_t)coordinates->position, record_used->length()), FALSE); (*record_used)[coordinates->position - 1] = TRUE; } /* if */ if (tap->is_pack) { ctws_state->substituted_parameter_pack = TRUE; } /* if */ } else if ((options & CTWS_ADJUST_COORDINATES) != 0) { ctws_state->substituted_parameter_pack |= constant_is_pack(con); } /* if */ } else { *copy_error = TRUE; } /* if */ } break; case tpck_member: /* A member constant, e.g., for a case like A::x. */ con_copy = copy_template_param_unknown_entity_con( con, template_arg_list, template_param_list, guide_type, /*is_address=*/FALSE, /*is_template_ref=*/FALSE, (a_template_arg_ptr)NULL, source_pos, ctws_state, options, copy_error, constant); break; case tpck_unknown_function: /* An unknown function. */ con_copy = copy_template_param_unknown_entity_con( con, template_arg_list, template_param_list, guide_type, /*is_address=*/TRUE, /*is_template_ref=*/FALSE, (a_template_arg_ptr)NULL, source_pos, ctws_state, options, copy_error, constant); break; case tpck_address: /* The template param constant represents the address of a member. Process the underlying tpck_member constant as an address, and use the result of that in place of both the tpck_address and the tpck_member. */ con_copy = copy_template_param_unknown_entity_con( con->variant.template_param.variant.constant, template_arg_list, template_param_list, guide_type, /*is_address=*/TRUE, /*is_template_ref=*/FALSE, (a_template_arg_ptr)NULL, source_pos, ctws_state, options, copy_error, constant); break; case tpck_sizeof: case tpck_datasizeof: case tpck_alignof: case tpck_uuidof: case tpck_typeid: case tpck_noexcept: /* The template param represents sizeof(T), __ALIGNOF__(T), __uuidof(T), typeid(T), or noexcept(T) where T is a type containing a template parameter. Determine the type of T after substitution. */ { an_expr_node_ptr expr = generic_sizeof_arg_expr(con); if (expr != NULL) { /* There's an associated expression. Do substitution on it. */ a_constant_ptr sizeof_expr_con = local_constant(); a_constant_ptr alloc_sizeof_expr_con; a_ctws_options_set extra_ctws_options = CTWS_NON_CONSTANT_EXPR; if (tpck_is(con, tpck_noexcept)) { extra_ctws_options |= CTWS_IS_RESCAN_OF_NOEXCEPT_OPERAND; } /* if */ expr = copy_template_param_expr(expr, template_arg_list, template_param_list, (a_type_ptr)NULL, source_pos, options | extra_ctws_options, copy_error, ctws_state, sizeof_expr_con, &alloc_sizeof_expr_con); if (expr == NULL) { /* The expression folds to a constant. */ a_constant_ptr new_con; if (alloc_sizeof_expr_con != NULL) { new_con = alloc_sizeof_expr_con; expr = alloc_node_for_allocated_constant(new_con); } else { new_con = sizeof_expr_con; expr = alloc_node_for_constant(new_con); } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (microsoft_bugs && new_con->ptr_to_mem_constant_construct && tpck_is(con, tpck_sizeof)) { /* Microsoft has a bug where rescanning an expression of the form sizeof(&X::m), where &X::m is a pointer-to-member, always fails. */ subst_fail(*copy_error); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ new_type = expr->type; release_local_constant(&sizeof_expr_con); } else { /* No associated expression, just a type. */ new_type = copy_type_with_substitution(con->variant.template_param. variant.templ_sizeof.type, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state); if (is_any_reference_type(new_type)) { new_type = type_pointed_to(new_type); } /* if */ } /* if */ if (same_entities( new_type, con->variant.template_param.variant.templ_sizeof.type) && expr == generic_sizeof_arg_expr(con)) { /* No change in the type or the expression, so the original constant is still okay. */ } else if (is_template_dependent_type(new_type)) { /* Still a template dependent type, so still need a tpck_sizeof/datasizeof/alignof/uuidof/typeid/noexcept constant. */ *constant = *con; if (con->variant.template_param.variant.templ_sizeof.type != NULL){ constant->variant.template_param.variant.templ_sizeof.type = new_type; } /* if */ constant->variant.template_param.variant.templ_sizeof.expr = expr; con_copy = NULL; } else { /* No longer a template parameter type, so the sizeof, alignof, uuidof, typeid, or noexcept result is known. */ a_type_ptr orig_new_type = new_type; new_type = skip_typerefs(new_type); complete_type_is_needed(new_type); #if MICROSOFT_EXTENSIONS_ALLOWED if (tpck_is(con, tpck_uuidof)) { /* __uuidof. */ make_uuidof_constant(new_type, constant); } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here. */ if (tpck_is(con, tpck_typeid)) { /* typeid(...). */ a_boolean is_cli_typeid = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && expr != NULL && expr->is_cli_typeid) { is_cli_typeid = TRUE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ make_typeid_constant(new_type, is_cli_typeid, constant); } else if (tpck_is(con, tpck_noexcept)) { /* noexcept(...). */ check_assertion(expr != NULL); set_unsigned_integer_constant( constant, (a_host_large_unsigned)!expr_might_throw(expr), bool_type()->variant.integer.int_kind); constant->type = bool_type(); } else { /* sizeof/alignof. */ complete_type_is_needed(new_type); if (is_incomplete_type(new_type) || is_function_type(new_type)) { subst_fail(*copy_error); } else { a_boolean template_case; a_host_large_unsigned value; if (tpck_is(con, tpck_sizeof)) { value = (a_host_large_unsigned)new_type->size; } else if (tpck_is(con, tpck_datasizeof)) { value = (a_host_large_unsigned)data_size_of_type(new_type); } else if (tpck_is(con, tpck_alignof)) { value = (a_host_large_unsigned) compute_alignof_value(orig_new_type, expr == NULL, expr, /*diag_pos=*/NULL, copy_error, &template_case); } else { unexpected_condition(); } /* if */ set_unsigned_integer_constant( constant, value, targ_size_t_int_kind); } /* if */ } /* if */ con_copy = NULL; } /* if */ } break; case tpck_template_ref: /* The template param constant represents a function template with a list of explicit template arguments. Process the underlying tpck_unknown_function constant with the explicit arguments. */ { a_constant_ptr templ_con = con->variant.template_param.variant.template_ref.con; a_template_arg_ptr arg_list = con->variant.template_param.variant. template_ref.arg_list; /* Do substitution on the template argument list. */ arg_list = copy_template_arg_list_with_substitution_rebuilding_arg_operands( (a_symbol_ptr)NULL, arg_list, (a_template_param_ptr)NULL, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state); /* Apply the template argument list to the template. */ con_copy = copy_template_param_unknown_entity_con( templ_con, template_arg_list, template_param_list, guide_type, /*is_address=*/TRUE, /*is_template_ref=*/TRUE, arg_list, source_pos, ctws_state, options, copy_error, constant); } break; case tpck_expression: /* The template param represents an expression that involves template parameters. Substitute the values of the template arguments and fold any constant operations that result. */ { a_constant_ptr base_con; a_boolean explicit_cast, keep_tpck_expression = FALSE; if (options & CTWS_COPY_ARG_OPERAND_INFO) { keep_tpck_expression = TRUE; options &= ~CTWS_COPY_ARG_OPERAND_INFO; } /* if */ if (is_template_param_cast_constant( con, &base_con, &explicit_cast)) { /* Handle some eok_cast/eok_ref_cast nodes specially, because they don't always have associated rescan info. */ con_copy = copy_template_param_cast_constant( con, base_con, explicit_cast, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state, constant); } else { an_expr_node_ptr expr = expr_node_from_tpck_expression(con), expr_copy; if (expr->type == type_of_unknown_templ_param_nontype && expr->compiler_generated && is_operation_node(expr) && node_operator_is(expr, eok_cast)) { /* Ignore compiler-generated casts that are there only to force an unknown type in the generic expression tree. */ expr = expr->variant.operation.operands; } /* if */ expr_copy = copy_template_param_expr( expr, template_arg_list, template_param_list, guide_type, source_pos, options, copy_error, ctws_state, constant, &con_copy); if (expr_copy != NULL && !*copy_error) { /* The expression isn't a constant. See if it can be interpreted. */ a_diag_list diag_list; clear_diag_list(&diag_list); if (interpret_expr(expr_copy, /*is_constant_evaluated=*/FALSE, /*force_prvalue=*/FALSE, constant, &diag_list)) { expr_copy = NULL; } /* if */ discard_more_info_list(&diag_list); } /* if */ if (expr_copy == NULL) { /* The expression folds to a constant. */ /* con_copy and constant are already set correctly. */ } else if ((!cpp11_sfinae_enabled && !expr_copy->is_lvalue) || (options & CTWS_IN_PARENT_SUBSTITUTION) != 0 || keep_tpck_expression) { /* The expression remains an expression. If the expression changed, make a new tpck_expression constant for it. With C++11 SFINAE, tpck_expression constants are rescanned, and this is usually not needed. It is required, however, if we're doing a parent substitution because subsequent substitution of nested template parameters is about to follow. We also sometimes wrap a top-level expression that cannot be folded when we do not yet know the template parameter that the result will be bound to (indicated via the option CTWS_COPY_ARG_OPERAND_INFO). */ if (expr != expr_copy) { make_template_param_expr_constant(expr_copy, constant); con_copy = NULL; } /* if */ } else { subst_fail(*copy_error); } /* if */ } /* if */ } break; case tpck_integer_pack: /* Process the underlying constant with the explicit arguments. */ { a_constant_ptr bound = con->variant.template_param.variant.bound, bound_copy; bound_copy = copy_template_param_con( bound, template_arg_list, template_param_list, guide_type, source_pos, options, copy_error, ctws_state, constant); if (!*copy_error) { if (bound_copy == NULL) { bound_copy = alloc_unshared_constant(constant); } /* if */ con_copy = fs_constant(ck_template_param); set_template_param_constant_kind( con_copy, tpck_integer_pack); con_copy->type = bound_copy->type; con_copy->variant.template_param.variant.bound = bound_copy; } /* if */ } break; case tpck_dependent_constant: /* The wrapper is only present to force the constant to be treated as dependent before substitution. Once substitution is requested, use the wrapped constant, applying any substitutions needed by its type. */ con_copy = copy_template_param_con( con->variant.template_param.variant.constant, template_arg_list, template_param_list, guide_type, source_pos, options, copy_error, ctws_state, constant); break; case tpck_concat_string_literals: { a_constant_ptr old_con = con->variant.template_param .variant.string_literal_list, new_list = NULL, last_con = NULL; for (; old_con != NULL && !*copy_error; old_con = old_con->next) { a_constant_ptr new_con; if (constant_is(old_con, ck_template_param)) { new_con = copy_template_param_con(old_con, template_arg_list, template_param_list, guide_type, source_pos, options, copy_error, ctws_state, constant); if (!*copy_error && new_con == NULL) { new_con = alloc_unshared_constant(constant); } /* if */ } else { new_con = alloc_unshared_constant(old_con); } /* if */ if (!*copy_error) { if (last_con == NULL) { new_list = new_con; } else { last_con->next = new_con; } /* if */ last_con = new_con; } /* if */ } /* for */ if (!*copy_error) { con_copy = fs_constant(ck_template_param); set_template_param_constant_kind(con_copy, tpck_concat_string_literals); con_copy->type = con->type; con_copy->character_kind = con->character_kind; con_copy->variant.template_param.variant.string_literal_list = new_list; } /* if */ } break; default: unexpected_condition_str("copy_template_param_con: unexpected kind"); } /* switch */ } else if (constant_is(con, ck_address) && con->variant.address.kind == abk_temporary && constant_is(con->variant.address.variant.constant, ck_template_param)) { /* A ck_address is sometimes added on top of a ck_template_param when binding, e.g., a nontype template parameter to a reference parameter. */ other_con = copy_template_param_con( con->variant.address.variant.constant, template_arg_list, template_param_list, (a_type_ptr)NULL, source_pos, options, copy_error, ctws_state, constant); if (!*copy_error) { new_type = copy_type_with_substitution(con->type, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state); } /* if */ if (!*copy_error) { if (other_con == NULL) { other_con = alloc_unshared_constant(constant); } /* if */ con_copy = NULL; copy_constant(con, constant); constant->type = new_type; constant->variant.address.variant.constant = other_con; } /* if */ } else if (constant_is(con, ck_reflection)) { copy_constant(con, constant); substitute_reflection_constant(constant, template_arg_list, template_param_list, options, ctws_state, source_pos, copy_error); con_copy = NULL; } else if (cpp11_sfinae_enabled && is_instantiation_dependent_type(con->type)) { /* A constant that is not a ck_template_param but that does have an instantiation-dependent type. Do substitution on it to see if there's an error. This may turn out to be a cast represented as a ck_aggregate constant (e.g., X::Y{3} is represented as a ck_aggregate constant of instantiation-dependent type, but if the substituted type ends up not being an aggregate, it should be treated as a cast). */ a_boolean is_template_arg = (options & CTWS_NONTYPE_TEMPLATE_ARG) != 0; if (is_template_arg) options &= ~CTWS_NONTYPE_TEMPLATE_ARG; new_type = copy_type_with_substitution(con->type, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state); if (!*copy_error) { if (con->explicit_cast_applied && constant_is(con, ck_aggregate) && !is_aggregate_type(new_type) && !is_instantiation_dependent_type(new_type)) { src_con = con->variant.aggregate.first_constant; if (src_con == NULL) { /* X{} where X is the non-aggregate type represented by new_type. */ if (!make_value_initialized_constant(new_type, constant)) { subst_fail(*copy_error); } /* if */ } else if (src_con->next != NULL) { /* More than one constant being cast. */ subst_fail(*copy_error); } else if (!substituted_cast_is_valid(src_con, new_type, con->explicit_cast_applied, is_template_arg, &reinterpret_cast_needed)) { /* The cast is not valid. */ subst_fail(*copy_error); } else { a_ctws_options_set new_options = options | CTWS_CAST_OPERAND | CTWS_EXPLICIT_CAST_OPERAND; other_con = copy_template_param_con( src_con, template_arg_list, template_param_list, new_type, source_pos, new_options, copy_error, ctws_state, constant); if (other_con != NULL) { copy_constant(other_con, constant); } /* if */ type_change_constant_full(constant, new_type, /*is_implicit_cast=*/FALSE, /*constant_context=*/TRUE, /*evaluated_context=*/TRUE, /*fold_constant_addr_exprs=*/TRUE, /*is_cli_attr_arg_expression=*/FALSE, /*check_cast_access=*/FALSE, /*check_ambiguity=*/TRUE, reinterpret_cast_needed, /*maintain_expression=*/FALSE, &did_not_fold, &error_detected, source_pos); if (error_detected != ec_no_error || did_not_fold) { subst_fail(*copy_error); } /* if */ } /* if */ } else { copy_constant(con, constant); constant->type = new_type; constant->expr = NULL; constant->is_generic_initializer = FALSE; } /* if */ con_copy = NULL; } /* if */ } else if (con->is_generic_initializer) { /* If the original constant was treated as a "generic initializer", create a copy without that flag since the substitution removes the "generic" aspect of that value. */ copy_constant(con, constant); constant->expr = NULL; constant->is_generic_initializer = FALSE; con_copy = NULL; } else { /* The original constant is returned. */ copy_constant_for_rescan_if_needed(&con_copy, constant, options); } /* if */ if (*copy_error) { /* Return an error constant on a copy error. */ con_copy = alloc_error_constant(); } /* if */ return con_copy; } /* copy_template_param_con */ a_constant_ptr copy_template_param_con_with_substitution( a_constant_ptr con, a_template_arg_ptr template_arg_list, a_template_param_ptr template_param_list, a_type_ptr template_param_type, a_source_position *source_pos, a_ctws_options_set options, a_boolean *copy_error, a_ctws_state_ptr ctws_state) /* Copy a ck_template_param constant, and return a pointer to the copy. If the constant is not a ck_template_param constant, just return a copy. In the process of copying, replace any template parameters with the corresponding values from the template argument list template_arg_list. template_param_list is the parameter list for which template_arg_list is an argument list. template_param_type, if non-NULL, is the type of the template parameter for which con is the actual argument. source_pos provides the source position for any calls of copy_type_with_substitution. If there is an error in the copying (specifically, if there is an error in doing substitution on a type), set *copy_error to TRUE. The copy of the constant is always placed in the file scope memory region. options is a set of name lookup options. */ { a_constant_ptr con_copy = NULL, base_con; a_constant_ptr constant = local_constant(); a_boolean explicit_cast; a_memory_region_number region_to_switch_back_to; switch_to_file_scope_region(®ion_to_switch_back_to); if (is_template_param_cast_constant(con, &base_con, &explicit_cast)) { an_expr_node_ptr expr = expr_node_from_tpck_expression(con); if (expr->extra.rescan_info != NULL) { /* Rescan information is available after all. Since the expression is a cast to the parameter type, we can retrieve the parameter type and proceed with substitution. */ if (template_param_type == NULL) { template_param_type = expr->type; } /* if */ } else { /* Handle some eok_cast/eok_ref_cast nodes specially, because they don't have associated rescan info. */ con_copy = copy_template_param_cast_constant( con, base_con, explicit_cast, template_arg_list, template_param_list, source_pos, options, copy_error, ctws_state, constant); goto done_copying; } /* if */ } /* if */ if (template_param_type != NULL && constant_is(con, ck_template_param) && tpck_is(con, tpck_expression)) { /* We know the template parameter type, so we can tell whether the argument is being used as an lvalue or an rvalue. We also have an expression for the argument, which may have been scanned without knowledge of how it would be used and might have to be adjusted now. */ an_expr_node_ptr expr = expr_node_from_tpck_expression(con); an_expr_node_ptr expr_copy; if (is_any_reference_type(template_param_type)) { /* The template parameter type is a reference. Copy the expression as an lvalue and then see if it has a constant address. */ check_assertion(is_any_lvalue_reference_type(template_param_type)); expr_copy = copy_template_param_expr_as_lvalue(expr, template_arg_list, template_param_list, template_param_type, source_pos, options, copy_error, ctws_state); if (!*copy_error) { check_assertion(expr_copy != NULL); if (is_glvalue_node(expr_copy)) { /* See if the glvalue has a constant address. */ if (constant_glvalue_address(expr_copy, constant, /*address_escapes=*/TRUE)) { /* Yes. Change the address constant type to a reference. */ a_type_ptr ref_type = make_reference_type( type_pointed_to(constant->type)); constant->type = ref_type; } else { /* The lvalue address is not constant. */ subst_fail(*copy_error); } /* if */ } else { /* The expression node is not an lvalue. */ subst_fail(*copy_error); } /* if */ } /* if */ } else { /* The template parameter type is not a reference, so copy the expression as an rvalue. */ expr_copy = copy_template_param_expr_as_rvalue(expr, template_arg_list, template_param_list, template_param_type, source_pos, options, copy_error, ctws_state, constant, &con_copy); if (!*copy_error && expr_copy != NULL) { /* We want a constant result, so convert to a constant (usually a tpck_expression constant). */ if (is_error_node(expr_copy)) { set_error_constant(constant); } else if (is_pointer_type(expr_copy->type) && constant_prvalue_pointer(expr_copy, constant, /*address_escapes=*/TRUE)) { /* The expression has constant pointer value (possibly template-dependent), so return that constant. */ } else if (is_template_dependent_type(expr_copy->type)) { make_template_param_expr_constant(expr_copy, constant); } else if (!fold_expr(expr_copy, constant)) { if (!(options & CTWS_NON_CONSTANT_EXPR) && !expr_is_instantiation_dependent(expr_copy)) { /* If we are in a context requiring a constant result and the substituted expression does not fold to a constant, the substitution effectively fails. */ set_error_constant(constant); subst_fail(*copy_error); } else { make_template_param_expr_constant(expr_copy, constant); } /* if */ } /* if */ } /* if */ } /* if */ if (*copy_error) { con_copy = con; } /* if */ } else { /* Just copy the constant with substitution. */ con_copy = copy_template_param_con(con, template_arg_list, template_param_list, template_param_type, source_pos, options, copy_error, ctws_state, constant); } /* if */ done_copying: if (con_copy == NULL && !*copy_error) { con_copy = alloc_shareable_constant(constant); } /* if */ switch_back_to_original_region(region_to_switch_back_to); release_local_constant(&constant); return con_copy; } /* copy_template_param_con_with_substitution */ void substitute_constant(a_constant_ptr *p_constant, a_type_ptr parent_class, a_template_param_ptr t_params, a_template_arg_ptr t_args, a_ctws_options_set options, a_ctws_state *ctws_state, a_source_position *source_pos, a_boolean *p_error) /* Replace *p_constant by an entry in which the given template parameters have been replaced by the given template arguments (if any). Furthermore, if parent_class (which may be NULL) is an instantiated template class of the form X, (recursively) perform the substitutions implied by the template arguments A1, A2, ... first. options and ctws_state provide state information for the substitution. *source_pos is the position associated with the substitution. *p_error is set to TRUE if a substitution error occurs. */ { if (parent_class != NULL && parent_class->variant.class_struct_union.is_template_class && !parent_class->variant.class_struct_union.is_specialized) { /* If the parent class is itself a template instance (but not an explicit specialization), first recursively substitute any parameters that it is associated with. */ a_template_arg_ptr parent_t_args = NULL; a_template_param_ptr parent_t_params; a_ctws_state parent_ctws_state; a_ctws_options_set parent_options = CTWS_PARTIAL_ARG_LIST_OKAY | CTWS_IN_PARENT_SUBSTITUTION; init_ctws_state(&parent_ctws_state); parent_ctws_state.in_parent_substitution = TRUE; get_substitution_pairs_for_template_class(parent_class, &parent_t_params, &parent_t_args); substitute_constant(p_constant, parent_class_or_null(parent_class), parent_t_params, parent_t_args, parent_options, &parent_ctws_state, source_pos, p_error); } /* if */ if (!*p_error && t_args != NULL) { *p_constant = copy_template_param_con_with_substitution( *p_constant, t_args, t_params, (a_type_ptr)NULL, source_pos, options, p_error, ctws_state); } /* if */ } /* substitute_constant */ void increment_template_dependent_enum_constant(a_constant_ptr con) /* The given constant has kind ck_template_param. We need to make it represent a constant that is the previous value incremented by one. */ { a_constant_ptr prev_val = alloc_unshared_constant(con); a_constant_ptr one_val = local_constant(); an_integer_kind one_kind = (an_integer_kind)ik_int; an_expr_node_ptr operands; a_memory_region_number region_to_switch_back_to; switch_to_file_scope_region(®ion_to_switch_back_to); clear_constant(con, (a_constant_repr_kind)ck_template_param); con->type = prev_val->type; if (!is_template_dependent_type(prev_val->type)) { /* Since we're not doing full semantics on the increment operation, force a dependent type on the original value. If we don't do this, back ends (including the C- and C++-generating back ends) may be surprised if they check for type consistency. */ prev_val->type = type_of_unknown_templ_param_nontype; } /* if */ con->variant.template_param.kind = (a_template_param_constant_kind)tpck_expression; /* Create a generic addition operation to increment the previous value. */ operands = alloc_node_for_constant(prev_val); if (is_integral_or_enum_type(prev_val->type)) { one_kind = skip_typerefs(prev_val->type)->variant.integer.int_kind; } /* if */ set_integer_constant(one_val, (a_host_large_integer)1, one_kind); operands->next = alloc_node_for_constant(one_val); con->variant.template_param.variant.expr = make_operator_node((an_expr_operator_kind)eok_add, con->type, operands); con->variant.template_param.variant.expr->compiler_generated = TRUE; release_local_constant(&one_val); switch_back_to_original_region(region_to_switch_back_to); } /* increment_template_dependent_enum_constant */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_boolean is_operator_returning_bool(an_expr_operator_kind op) /* Return TRUE iff the indicated operator returns a bool (C++) or 0/1 int (C) result. */ { a_boolean returns_bool; switch (op) { case eok_land: case eok_lor: case eok_not: case eok_eq: case eok_ne: case eok_gt: case eok_lt: case eok_ge: case eok_le: case eok_bool_cast: returns_bool = TRUE; break; default: returns_bool = FALSE; break; } /* switch */ return returns_bool; } /* is_operator_returning_bool */ #if PARENS_IN_IL an_expr_node_ptr f_skip_parens(an_expr_node_ptr expr) /* Strip parentheses (eok_parens nodes) off an expression, and return the underlying expression. If the argument passed in is NULL, return NULL. */ { if (expr != NULL) { while (is_operation_node(expr) && node_operator_is(expr, eok_parens)) { expr = expr->variant.operation.operands; } /* while */ } /* if */ return expr; } /* f_skip_parens */ #endif /* PARENS_IN_IL */ #if !STANDALONE_UTILITY_PROGRAM an_expr_node_ptr add_cast(an_expr_node_ptr node, a_type_ptr new_type) /* Add a cast to new_type to the node and return the cast node. new_type should not have any top-level type qualifiers. The cast is marked as compiler-generated. */ { an_expr_node_ptr expr = make_operator_node((an_expr_operator_kind)eok_cast, new_type, node); expr->compiler_generated = TRUE; return expr; } /* add_cast */ an_expr_node_ptr add_cast_if_necessary(an_expr_node_ptr node, a_type_ptr new_type) /* Add a cast to new_type to the node and return the cast node. If the type of the node is already new_type return the original node. new_type should not have any top-level type qualifiers. If a cast is added it is marked as compiler-generated. */ { if (!cast_identical_types(node->type, new_type)) { node = add_cast(node, new_type); } /* if */ return node; } /* add_cast_if_necessary */ an_expr_node_ptr add_cast_to_glvalue(an_expr_node_ptr node, a_type_ptr type) /* Cast the glvalue expression given by node to the type specified by type. This adjusts the type of the glvalue without creating a new object. The operation is marked as compiler-generated. The result has the same value category (lvalue or xvalue) as the original expression. Note that an lvalue cast cannot handle base class casts, only cv-qualification or other non-base-class type adjustment. */ { an_expr_node_ptr new_node; check_assertion(is_glvalue_node(node) || is_error_node(node)); if (node_is_operator(node, eok_lvalue_adjust)) { new_node = node; new_node->type = type; } else { new_node = make_lvalue_operator_node(eok_lvalue_adjust, type, node); copy_node_value_category(node, new_node); new_node->compiler_generated = TRUE; new_node->position = node->position; } /* if */ return new_node; } /* add_cast_to_glvalue */ an_expr_node_ptr add_cast_to_glvalue_if_necessary(an_expr_node_ptr node, a_type_ptr type) /* Cast the glvalue expression given by node to the type specified by type, but do nothing if the node already has the desired type. This adjusts the type of the glvalue without creating a new object. The operation is marked as compiler-generated. The result has the same value category (lvalue or xvalue) as the original expression. Note that an lvalue cast cannot handle base class casts, only minor cv-qualification or type adjustment. */ { if (!cast_identical_types(node->type, type)) { node = add_cast_to_glvalue(node, type); } /* if */ return node; } /* add_cast_to_glvalue_if_necessary */ an_expr_node_ptr add_rvalue_class_adjust_node(an_expr_node_ptr node, a_type_ptr type) /* Add an eok_class_rvalue_adjust node to the indicated expression, to adjust its type to "type", and return a pointer to the resulting expression. */ { check_assertion(!is_glvalue_node(node) && is_class_struct_union_type(type)); if (is_operation_node(node) && node->compiler_generated && node_operator_is(node, eok_base_class_cast)) { /* A base class adjustment can be folded into a base class cast. */ node->type = type; } else { node = make_operator_node((an_expr_operator_kind)eok_class_rvalue_adjust, type, node); node->compiler_generated = TRUE; } /* if */ return node; } /* add_rvalue_class_adjust_node */ an_expr_node_ptr make_dummy_lvalue_expr(a_type_ptr type) /* Make a placeholder lvalue expression whose type is "type". */ { an_expr_node_ptr expr; a_constant_ptr zero_con = local_constant(); a_type_ptr ptr_type = make_pointer_type(type); if (is_template_dependent_type(type)) { /* Force a template-dependent constant for the dependent type case. */ a_constant_ptr con; make_zero_of_proper_type(integer_type((an_integer_kind)ik_int), zero_con); con = alloc_shareable_constant(zero_con); make_template_param_cast_constant(con, zero_con, ptr_type, /*is_explicit=*/FALSE); #if BACK_END_IS_CP_GEN_BE /* Prevent the C++-generating back end from putting out the constant as simply 0. */ zero_con->variant.template_param.variant.expr-> keep_as_cast_for_cp_gen_be = TRUE; #endif /* BACK_END_IS_CP_GEN_BE */ } else { /* Normal non-dependent case. */ make_zero_of_proper_type(ptr_type, zero_con); } /* if */ expr = alloc_node_for_constant(zero_con); expr = add_indirection_to_node(expr); release_local_constant(&zero_con); return expr; } /* make_dummy_lvalue_expr */ static an_expr_node_ptr make_dummy_expr(a_type_ptr type, a_boolean is_lvalue) /* Make a placeholder expression whose type is "type" and whose lvalueness matches is_lvalue. */ { an_expr_node_ptr expr; expr = make_dummy_lvalue_expr(type); if (!is_lvalue) { expr = rvalue_expr_for_lvalue(expr); } /* if */ return expr; } /* make_dummy_expr */ static a_lambda_capture_ptr copy_lambda_capture( a_lambda_capture_ptr capture, an_expr_copy_options_set options, a_tree_copy_control_block *cblock) /* Allocate a copy of a lambda capture and return a pointer to it. */ { a_lambda_capture_ptr capture_copy; capture_copy = alloc_lambda_capture(); *capture_copy = *capture; if (in_file_scope(capture_copy) && !in_file_scope(capture)) { /* Normally it'd be fine to continue to refer to the original nodes, however since the capture copy is in file scope, it can't refer to nodes in function scope - copy the appropriate nodes instead. This should only be possible for init captures - a local lambda with non-init captures should never have to be copied into file-scope memory, and this case should have been caught sooner. */ check_assertion(capture->is_init_capture); capture_copy->captured.initializer = i_copy_dynamic_init(capture->captured.initializer, options, cblock); } /* if */ return capture_copy; } /* copy_lambda_capture */ static a_lambda_ptr copy_lambda(a_lambda_ptr lambda, an_expr_copy_options_set options, a_tree_copy_control_block *cblock) /* Allocate a copy of a lambda and return a pointer to it. */ { a_lambda_ptr lambda_copy; a_lambda_capture_ptr capture, capture_copy, prev = NULL; lambda_copy = alloc_lambda(); *lambda_copy = *lambda; for (capture = lambda->capture_list; capture != NULL; capture = capture->next) { capture_copy = copy_lambda_capture(capture, options, cblock); capture_copy->next = NULL; if (prev == NULL) { lambda_copy->capture_list = capture_copy; } else { prev->next = capture_copy; } /* if */ prev = capture_copy; } /* for */ return lambda_copy; } /* copy_lambda */ an_expr_node_ptr copy_node(an_expr_node_ptr expr) /* Allocate a copy of an expression node and return a pointer to it. */ { an_expr_node_ptr expr_copy; an_expr_node_kind kind =enum_cast(expr->kind); a_new_delete_supplement_ptr copy_new_delete = NULL; #if MICROSOFT_EXTENSIONS_ALLOWED a_gcnew_supplement_ptr copy_gcnew = NULL; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ a_throw_supplement_ptr copy_throw_info = NULL; a_condition_supplement_ptr copy_condition = NULL; #if DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING an_eh_prologue_supplement_ptr copy_prologue_info = NULL; #endif /* DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING */ expr_copy = alloc_expr_node(kind); /* Preserve the supplement pointer if there is one. */ if (kind == (an_expr_node_kind)enk_new_delete) { copy_new_delete = expr_copy->variant.new_delete; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (kind == (an_expr_node_kind)enk_gcnew) { copy_gcnew = expr_copy->variant.gcnew_info; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else if (kind == (an_expr_node_kind)enk_throw) { copy_throw_info = expr_copy->variant.throw_info; } else if (kind == (an_expr_node_kind)enk_condition) { copy_condition = expr_copy->variant.condition; #if DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING } else if (kind == (an_expr_node_kind)enk_lowered_eh_construct) { set_lowered_eh_construct_node_kind(expr_copy, expr->variant.lowered_eh.kind); if (expr->variant.lowered_eh.kind == (a_lowered_eh_construct_kind)leck_function_prologue) { copy_prologue_info = expr_copy->variant.lowered_eh.variant.prologue_info; } /* if */ #endif /* DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING */ } /* if */ /* Copy the node. */ *expr_copy = *expr; expr_copy->next = NULL; expr_copy->result_is_not_used = FALSE; expr_copy->is_pack_expansion = FALSE; expr_copy->is_shallow_copy = TRUE; expr_copy->extra.rescan_info = NULL; if (kind == (an_expr_node_kind)enk_new_delete) { /* Copy the new/delete supplement. */ *copy_new_delete = *expr->variant.new_delete; expr_copy->variant.new_delete = copy_new_delete; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (kind == (an_expr_node_kind)enk_gcnew) { /* Copy the gcnew supplement. */ *copy_gcnew = *expr->variant.gcnew_info; expr_copy->variant.gcnew_info = copy_gcnew; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else if (kind == (an_expr_node_kind)enk_throw) { /* Copy the throw supplement. */ if (expr->variant.throw_info != NULL) { *copy_throw_info = *expr->variant.throw_info; } else { /* Rethrow; discard the throw supplement on the copy. */ copy_throw_info = NULL; } /* if */ expr_copy->variant.throw_info = copy_throw_info; } else if (kind == (an_expr_node_kind)enk_condition) { /* Copy the condition supplement. */ *copy_condition = *expr->variant.condition; expr_copy->variant.condition = copy_condition; #if DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING } else if (copy_prologue_info != NULL) { /* Copy the EH prologue supplement. */ *copy_prologue_info = *expr_copy->variant.lowered_eh.variant.prologue_info; expr_copy->variant.lowered_eh.variant.prologue_info = copy_prologue_info; #endif /* DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING */ } /* if */ return expr_copy; } /* copy_node */ static an_expr_node_ptr i_copy_list_of_expr_trees( an_expr_node_ptr expr_list, an_expr_copy_options_set options, a_tree_copy_control_block *cblock) /* Make a copy of a list of expression trees and return a pointer to it. options is a set of options for the copy. cblock is a control block for the copy. This is an internal routine (thus the "i_" prefix); copy_list_of_expr_trees should be called to start a copy. */ { an_expr_node_ptr expr, expr_copy, prev_expr_copy, expr_list_copy; expr_list_copy = prev_expr_copy = NULL; for (expr = expr_list; expr != NULL; expr = expr->next) { expr_copy = i_copy_expr_tree(expr, options, cblock); if (expr_list_copy == NULL) { expr_list_copy = expr_copy; } else { check_assertion(prev_expr_copy != NULL); prev_expr_copy->next = expr_copy; } /* if */ prev_expr_copy = expr_copy; } /* for */ return expr_list_copy; } /* i_copy_list_of_expr_trees */ an_expr_node_ptr copy_list_of_expr_trees(an_expr_node_ptr expr_list, an_expr_copy_options_set options) /* Make a copy of a list of expression trees and return a pointer to it. options is a set of options for the copy. */ { a_tree_copy_control_block cblock; an_expr_node_ptr expr_list_copy; /* This is a wrapper around i_copy_list_of_expr_trees that initializes the control block to be used for the entire copy. */ clear_tree_copy_control_block(&cblock); expr_list_copy = i_copy_list_of_expr_trees(expr_list, options, &cblock); done_with_tree_copy_control_block(&cblock); return expr_list_copy; } /* copy_list_of_expr_trees */ static a_boolean lambda_expr_captures_something(a_lambda_ptr lambda) /* Return TRUE if the provided lambda expression captures something, FALSE otherwise. An init capture is not considered to be capturing something. */ { a_boolean result = FALSE; a_lambda_capture_ptr lcp = lambda->capture_list; for (; lcp != NULL; lcp = lcp->next) { if (!lcp->is_init_capture) { result = TRUE; break; } /* if */ } /* for */ return result; } /* lambda_expr_captures_something */ static an_expr_node_ptr i_copy_expr_tree(an_expr_node_ptr expr, an_expr_copy_options_set options, a_tree_copy_control_block *cblock) /* Make a copy of an expression tree and return a pointer to it. options is a set of options for the copy. cblock is a control block for the copy. This is an internal routine (thus the "i_" prefix); copy_expr_tree should be called to start a copy. When CE_COPYING_FOR_LOCAL_EXPR_NODE_REF is set, an expression node already copied in this operation is reused rather than copied again. */ { an_expr_node_ptr expr_copy = NULL; a_new_delete_supplement_ptr ndsp, copy_ndsp; #if MICROSOFT_EXTENSIONS_ALLOWED a_gcnew_supplement_ptr gsp, copy_gsp; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (options & CE_COPYING_FOR_LOCAL_EXPR_NODE_REF) { expr_copy = (an_expr_node_ptr)find_copy_remap_address((char*)expr, cblock); if (expr_copy != NULL) goto end_of_routine; } /* if */ /* Copy the top node. */ expr_copy = copy_node(expr); if (options & (CE_COPYING_FOR_CONSTEXPR_FOLDING | CE_PRESERVE_RESCAN_INFO)) { expr_copy->extra.rescan_info = expr->extra.rescan_info; expr_copy->is_pack_expansion = expr->is_pack_expansion; } /* if */ switch (expr->kind) { case enk_error: case enk_variable: case enk_field: case enk_routine: case enk_address_of_ellipsis: #if VLA_DEALLOCATIONS_IN_IL case enk_vla_dealloc: #endif /* VLA_DEALLOCATIONS_IN_IL */ case enk_type_operand: case enk_param_ref: case enk_concept_id: /* Nothing more to copy. */ break; case enk_constant: if ((options & CE_COPYING_FOR_CONSTEXPR_MASTER_EXPR) || (options & CE_COPYING_FOR_LOCAL_EXPR_NODE_REF) || node_constant(expr)->part_of_constexpr_master_expr || (!in_file_scope(node_constant(expr)) && (in_file_scope(expr_copy) || (options & (CE_DOING_INLINING_OF_FUNCTION_CALL | CE_COPYING_FROM_ONE_FUNC_TO_ANOTHER))))) { /* Copy a constant to avoid having an expression in the file-scope memory region pointing to a constant in a function scope memory region. Also copy function-scope constants when copying for inlining or for a local-expr-node-ref, because those copies land in a different function-scope memory region. */ an_expr_copy_options_set subcopy_options = options; subcopy_options |= CE_COPIED_CONSTANTS_MAY_BE_SHARED; node_constant(expr_copy) = i_copy_constant_full(node_constant(expr), (a_constant *)NULL, subcopy_options, cblock); } /* if */ break; case enk_operation: /* Copy the operands of the operation. */ #if MINIMAL_INLINING /* Some short-circuited operations can be simplified while they are copied if the first operand value is constant. */ if ((options & CE_DOING_INLINING_OF_FUNCTION_CALL) && copy_and_simplify_short_circuited_operation(expr_copy, &cblock->inlining_failed)) { break; } /* if */ #endif /* MINIMAL_INLINING */ expr_copy->variant.operation.operands = i_copy_list_of_expr_trees(expr->variant.operation.operands, options, cblock); if (expr->variant.operation.kind == eok_comma) { /* The value of the first operand of a comma operator is not used. */ set_expr_result_not_used(expr_copy->variant.operation.operands); #if MINIMAL_INLINING if ((options & CE_DOING_INLINING_OF_FUNCTION_CALL) && expr_copy->orig_lvalue_type != NULL && !node_includes_glvalue_to_prvalue_conv( expr_copy->variant.operation.operands)) { /* After remapping a variable under a comma expression during inlining, the substituted expression might not include a glvalue-to-prvalue conversion anymore, and thus no orig_lvalue_type should be recorded, because that confuses node_operands_have_correct_value_category. */ expr_copy->orig_lvalue_type = NULL; } /* if */ #endif /* MINIMAL_INLINING */ } /* if */ break; case enk_temp_init: /* Copy the dynamic init for a dynamic initialization. */ expr_copy->variant.init.dynamic_init = i_copy_dynamic_init(expr->variant.init.dynamic_init, options, cblock); /* If the dynamic initialization is attached to a static object lifetime, make the temporary static too. */ { an_object_lifetime_ptr lifetime = expr_copy->variant.init.dynamic_init->lifetime; if (lifetime != NULL) { if (is_static_object_lifetime_kind(lifetime->kind)) { expr_copy->variant.init.dynamic_init->static_temp = TRUE; } /* if */ } /* if */ } /* if */ break; case enk_new_delete: /* Copy the subtree and dynamic init for a new/delete operation. */ /* Note that the new/delete supplement was copied by copy_node. */ ndsp = expr->variant.new_delete; copy_ndsp = expr_copy->variant.new_delete; /* Copy the number_of_elements node before arg below because that's how it would appear in a left-to-right traversal (since number_of_elements might have a destructible entity and is evaluated before arg). */ if (ndsp->number_of_elements != NULL) { copy_ndsp->number_of_elements = i_copy_expr_tree(ndsp->number_of_elements, options, cblock); } /* if */ if (ndsp->arg != NULL) { copy_ndsp->arg = i_copy_list_of_expr_trees(ndsp->arg, options, cblock); } /* if */ if (ndsp->freeing_of_storage_on_exception != NULL) { copy_ndsp->freeing_of_storage_on_exception = i_copy_dynamic_init(ndsp->freeing_of_storage_on_exception, options, cblock); } /* if */ if (ndsp->dynamic_init != NULL) { copy_ndsp->dynamic_init = i_copy_dynamic_init(ndsp->dynamic_init, options, cblock); } /* if */ break; case enk_lambda: /* Make a copy of the lambda and its initialization. */ if (!in_file_scope(expr->variant.init.source.lambda) && in_file_scope(expr_copy) && lambda_expr_captures_something(expr->variant.init.source.lambda)) { /* A local lambda with captures (except for init captures) should never have to be copied into file-scope memory (it would cause memory region problems with the captures), but we may get here in severe error cases. */ expect_error(); expr_copy = error_node(); } else { expr_copy->variant.init.dynamic_init = i_copy_dynamic_init(expr->variant.init.dynamic_init, options, cblock); if (expr->variant.init.dynamic_init->kind == (a_dynamic_init_kind)dik_lambda) { /* The lambda is already copied as part of the dynamic initializer copy. Use that copy here instead of re-copying it. */ expr_copy->variant.init.source.lambda = expr_copy->variant.init.dynamic_init->variant.constant.lambda; } else { expr_copy->variant.init.source.lambda = copy_lambda(expr->variant.init.source.lambda, options, cblock); } /* if */ } /* if */ break; #if MICROSOFT_EXTENSIONS_ALLOWED case enk_gcnew: /* Copy the subtree and dynamic init for a new/delete operation. */ /* Note that the gcnew supplement was copied by copy_node. */ gsp = expr->variant.gcnew_info; copy_gsp = expr_copy->variant.gcnew_info; if (gsp->cli_array_dimension_lengths != NULL) { copy_gsp->cli_array_dimension_lengths = i_copy_list_of_expr_trees( gsp->cli_array_dimension_lengths, options, cblock); } /* if */ if (gsp->dynamic_init != NULL) { copy_gsp->dynamic_init = i_copy_dynamic_init(gsp->dynamic_init, options, cblock); } /* if */ break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case enk_throw: if (expr->variant.throw_info != NULL) { /* Copy the dynamic init for a throw. */ expr_copy->variant.throw_info->dynamic_init = i_copy_dynamic_init(expr->variant.throw_info->dynamic_init, options, cblock); #if DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING if (expr->variant.throw_info->expr != NULL) { expr_copy->variant.throw_info->expr = i_copy_expr_tree(expr->variant.throw_info->expr, options, cblock); } /* if */ #endif /* DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING */ } /* if */ break; case enk_condition: { a_condition_supplement_ptr dst_csp = expr_copy->variant.condition, src_csp = expr->variant.condition; if (src_csp->initialization != NULL) { /* We should never have to copy a condition declaration with a C++17 initialization statement. */ if (options & CE_DOING_INLINING_OF_FUNCTION_CALL) { cblock->inlining_failed = TRUE; break; } else { unexpected_condition(); } /* if */ } /* if */ /* Copy the dynamic init and the expression. */ dst_csp->dynamic_init = i_copy_dynamic_init(src_csp->dynamic_init, options, cblock); dst_csp->expr = i_copy_expr_tree(src_csp->expr, options, cblock); } /* if */ break; case enk_object_lifetime: /* For an object lifetime, create a new object lifetime for the copy. */ #if MINIMAL_INLINING { a_boolean need_to_pop_function_lifetime = FALSE; if (options & CE_DOING_INLINING_OF_FUNCTION_CALL) { /* When doing inlining, we might be expanding a function that has object lifetimes into a function that has none. If so, we need to add an object lifetime to the current function. */ if (!in_file_scope(expr_copy)) { check_assertion(innermost_function_scope != NULL); if (innermost_function_scope->lifetime == NULL) { /* Add an object lifetime for the function. */ push_object_lifetime(iek_scope, (char *)innermost_function_scope, (an_object_lifetime_kind)olk_block); need_to_pop_function_lifetime = TRUE; } /* if */ } /* if */ } /* if */ #endif /* MINIMAL_INLINING */ check_assertion_str(curr_object_lifetime != NULL, "i_copy_expr_tree: curr_object_lifetime is NULL"); check_assertion_str(expr->variant.object_lifetime.ptr != NULL, "i_copy_expr_tree: enk_object_lifetime has NULL lifetime"); if (curr_object_lifetime->kind == (an_object_lifetime_kind)olk_expr_temporary) { check_assertion(expr->variant.object_lifetime.ptr->kind == (an_object_lifetime_kind)olk_expr_temporary); /* We're already inside an expr temporary lifetime and we would be pushing another. Ignore this inner lifetime. This can come up with inlining. */ expr_copy = i_copy_expr_tree(expr->variant.object_lifetime.expr, options, cblock); } else { push_object_lifetime(iek_none, (char *)NULL, expr->variant.object_lifetime.ptr->kind); expr_copy->variant.object_lifetime.expr = i_copy_expr_tree(expr->variant.object_lifetime.expr, options, cblock); expr_copy->variant.object_lifetime.ptr = NULL; bind_object_lifetime(curr_object_lifetime, iek_expr_node, (char *)expr_copy); (void)pop_object_lifetime(); if (expr_copy->variant.object_lifetime.ptr == NULL) { /* The copied lifetime was useless and was deleted. This can happen when the source lifetime contains no destructions, which comes up when placement delete is lowered. Eliminate the enk_object_lifetime node in the copy. */ expr_copy = expr_copy->variant.object_lifetime.expr; } /* if */ } /* if */ #if MINIMAL_INLINING if (need_to_pop_function_lifetime) (void)pop_object_lifetime(); } #endif /* MINIMAL_INLINING */ break; case enk_typeid: expr_copy->variant.typeid_info.type_with_opt_expr = i_copy_list_of_expr_trees(expr->variant.typeid_info.type_with_opt_expr, options, cblock); break; case enk_sizeof: case enk_datasizeof: case enk_alignof: /* If there is an expression, copy it. */ if (!expr->variant.sizeof_info.is_type) { if (in_file_scope(expr_copy) && !in_file_scope(expr)) { /* Potential memory region problem -- the expression under the sizeof may refer to function-local variables, so drop the expression and just use the type. This comes up when copying VLA bound expressions that are overall constant, but have a nonconstant part under a sizeof. */ expr_copy->variant.sizeof_info.is_type = TRUE; expr_copy->variant.sizeof_info.variant.type = expr->variant.sizeof_info.variant.expr->type; } else { expr_copy->variant.sizeof_info.variant.expr = i_copy_expr_tree(expr->variant.sizeof_info.variant.expr, options, cblock); } /* if */ } /* if */ break; case enk_sizeof_pack: /* If there is an expression, copy it. */ if (!expr->variant.sizeof_pack.is_type && !expr->variant.sizeof_pack.is_template_template) { expr_copy->variant.sizeof_pack.variant.expr = i_copy_expr_tree(expr->variant.sizeof_pack.variant.expr, options, cblock); } /* if */ break; #if DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING case enk_lowered_eh_construct: /* Nothing to copy. */ break; #endif /* DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING */ #if DO_IL_LOWERING && ABI_CHANGES_FOR_COVARIANT_VIRTUAL_FUNC_RETURN case enk_result_of_overriding_function: /* Nothing to copy. */ break; #endif /* DO_IL_LOWERING && ABI_CHANGES_FOR_COVARIANT_VIRTUAL_FUNC_RETURN */ case enk_reuse_value: { /* Change the dynamic init pointer to the copy of that entry. */ char *copy = find_copy_remap_address( (char *)expr->variant.reused_value_init, cblock); /* We don't expect that the part of the tree with the reuse will get copied independently of the part that defines the reusable value. */ check_assertion(copy != NULL); expr_copy->variant.reused_value_init = (a_dynamic_init_ptr)copy; } break; case enk_builtin_operation: /* Copy the operands of the operation. */ expr_copy->variant.builtin_operation.operands = i_copy_list_of_expr_trees(expr->variant.builtin_operation.operands, options, cblock); break; case enk_braced_init_list: expr_copy->variant.braced_init_list = i_copy_list_of_expr_trees(expr->variant.braced_init_list, options, cblock); break; case enk_c11_generic: expr_copy->variant.c11_generic.operands = i_copy_list_of_expr_trees(expr->variant.c11_generic.operands, options, cblock); /* Update the variant.c11_generic_operands.result pointer in the copy. */ { an_expr_node_ptr node1 = expr->variant.c11_generic.operands, node2 = expr_copy->variant.c11_generic.operands; while (node1 != expr->variant.c11_generic.result) { node1 = node1->next; node2 = node2->next; } /* while */ expr_copy->variant.c11_generic.result = node2; } break; #if BUILTIN_FUNCTIONS_ENABLED case enk_builtin_choose_expr: expr_copy->variant.builtin_choose_expr.operands = i_copy_list_of_expr_trees(expr->variant.builtin_choose_expr.operands, options, cblock); expr_copy->variant.builtin_choose_expr.choose_first = expr->variant.builtin_choose_expr.choose_first; break; #endif /* BUILTIN_FUNCTIONS_ENABLED */ case enk_fold: expr_copy->variant.fold.operands = i_copy_list_of_expr_trees(expr->variant.fold.operands, options, cblock); expr_copy->variant.fold.operator_token = expr->variant.fold.operator_token; expr_copy->variant.fold.left_associative = expr->variant.fold.left_associative; break; case enk_yield: case enk_await: expr_copy->variant.await_info.operand = i_copy_expr_tree(expr->variant.await_info.operand, options, cblock); expr_copy->variant.await_info.ready_resume_suspend = i_copy_list_of_expr_trees(expr->variant.await_info. ready_resume_suspend, options, cblock); break; case enk_requires: expr_copy->variant.requires_expr.requirements = i_copy_list_of_expr_trees( expr->variant.requires_expr.requirements, options, cblock); expr_copy->variant.requires_expr.parameters = expr->variant.requires_expr.parameters; break; case enk_compound_req: expr_copy->variant.compound_req.expr_and_constraint = i_copy_list_of_expr_trees( expr->variant.compound_req.expr_and_constraint, options, cblock); break; case enk_nested_req: expr_copy->variant.nested_req.constraint = i_copy_list_of_expr_trees( expr->variant.nested_req.constraint, options, cblock); break; case enk_initializer: expr_copy->variant.initializer.dyn_init = i_copy_dynamic_init(expr->variant.initializer.dyn_init, options, cblock); break; #if BUILTIN_FUNCTIONS_ENABLED case enk_const_eval_deferred: { auto &reattempt_state = expr->variant.const_eval_deferred.reattempt_state; if (options & CE_CONST_EVAL_SUB_EXPRESSION) { /* This is being copied for evaluation as a sub expression of a deferred constant evaluation that's now being evaluated. To ensure there's only one evaluation performed, and that it's the correct evaluation, unwrap the expression. */ expr_copy = i_copy_expr_tree( expr->variant.const_eval_deferred.wrapped, options, cblock); } else if ((reattempt_state.default_arg && (options & CE_COPYING_EVALUATED_DEFAULT_ARG_EXPR)) || (reattempt_state.default_mem_init && (options & CE_COPYING_DEFAULT_MEMBER_INIT))) { /* A call to a builtin source location operation is being performed in a default argument list and that list is being copied. The source location to be used is the call to the function with the default arguments so fold the expression now. */ an_expr_node_ptr wrapped = i_copy_expr_tree( expr->variant.const_eval_deferred.wrapped, options | CE_CONST_EVAL_SUB_EXPRESSION, cblock); a_diag_list diag_list; a_constant_ptr constant = local_constant(); clear_diag_list(&diag_list); if (interpret_expr(wrapped, /*is_constant_evaluated=*/TRUE, /*force_prvalue=*/FALSE, constant, &diag_list)) { expr_copy = alloc_node_for_constant(constant); } else if (!is_template_dependent_context() && expr_error_should_be_issued() && !is_consteval_diag_deferred()) { a_diagnostic_ptr dp = pos_start_error(ec_expr_not_constant, &wrapped->position); add_more_info_list(dp, &diag_list); end_diagnostic(dp); expr_copy = error_node(); } /* if */ release_local_constant(&constant); } else { /* Copy the subtree to ensure it is in the right memory region. */ expr_copy->variant.const_eval_deferred.wrapped = i_copy_expr_tree(expr->variant.const_eval_deferred.wrapped, options, cblock); } /* if */ } break; case enk_token_sequence: expr_copy = alloc_node_for_constant(fs_constant(ck_reflection)); expr_copy->type = expr->type; node_constant(expr_copy)->type = expr_copy->type; expr_copy->position = expr->position; break; #endif /* BUILTIN_FUNCTIONS_ENABLED */ case enk_pack_index: expr_copy->variant.pack_index.expr = i_copy_expr_tree(expr->variant.pack_index.expr, options, cblock); expr_copy->variant.pack_index.index_expr = i_copy_expr_tree(expr->variant.pack_index.index_expr, options, cblock); break; case enk_statement: /* Doesn't have to be copied because forbidden in default argument expressions and because functions containing statement expressions are considered not to be inlinable. Copying these would involve copying the statement subtree, the associated scopes, the variables in those scopes, the initializers on those variables... */ if (options & CE_COPY_NOT_EVALUATED) { /* If the statement expression occurs in a context where it's not evaluated, create a dummy expression of the appropriate type and lvalueness. */ expr_copy = make_dummy_expr(expr->type, expr->is_lvalue); break; } /* if */ FALLTHROUGH /* Do not insert code here. */ default: unexpected_condition_str("i_copy_expr_tree: bad expr kind"); } /* switch */ #if MINIMAL_INLINING if (options & CE_DOING_INLINING_OF_FUNCTION_CALL) { /* When doing inlining, look for parameters that should be remapped. */ adjust_copied_expression_for_inlining(expr_copy, &cblock->inlining_failed); } /* if */ #endif /* MINIMAL_INLINING */ if (options & CE_COPYING_EVALUATED_DEFAULT_ARG_EXPR) { do_instantiations_for_copied_default_arg_expr(expr_copy); } /* if */ if (options & CE_COPYING_FOR_LOCAL_EXPR_NODE_REF) { /* Record the copy of this node so that another reference to expr in this copy operation reuses it. Some kinds of nodes are copied as a node other than the one returned by copy_node, so this is done once expr_copy has its final value. A node is never reached again while its own copy is in progress: The sharing comes from the expressions recorded for folded constants, and such an expression always predates the constants that point to it. */ add_copy_remap_entry((char*)expr, (char*)expr_copy, cblock); } /* if */ end_of_routine:; return expr_copy; } /* i_copy_expr_tree */ an_expr_node_ptr copy_expr_tree(an_expr_node_ptr expr, an_expr_copy_options_set options) /* Make a copy of an expression tree and return a pointer to it. options is a set of options for the copy. */ { an_expr_node_ptr expr_copy = NULL; if (expr != NULL) { a_tree_copy_control_block cblock; /* This is a wrapper around i_copy_expr_tree that initializes the control block to be used for the entire copy. */ clear_tree_copy_control_block(&cblock); expr_copy = i_copy_expr_tree(expr, options, &cblock); done_with_tree_copy_control_block(&cblock); } /* if */ return expr_copy; } /* copy_expr_tree */ #if MINIMAL_INLINING an_expr_node_ptr copy_expr_tree_for_inlining(an_expr_node_ptr expr, a_boolean *inlining_failed) /* Return a copy of the specified expression tree with remapped parameter variables replaced with appropriate values for the invocation of the function currently being inlined. copy_expr_tree calls back to adjust_copied_expression_for_inlining to perform the appropriate remappings. A lowering post pass is performed on the resulting expression (to address any optimization issues that may arise as a result of the variable remapping). *inlining_failed is set to TRUE when the expression cannot be inlined. */ { a_tree_copy_control_block cblock; an_expr_node_ptr expr_copy; clear_tree_copy_control_block(&cblock); expr_copy = i_copy_expr_tree(expr, CE_DOING_INLINING_OF_FUNCTION_CALL, &cblock); done_with_tree_copy_control_block(&cblock); if (cblock.inlining_failed) { *inlining_failed = TRUE; } else { perform_post_pass_on_lowered_expression(expr_copy); } /* if */ return expr_copy; } /* copy_expr_tree_for_inlining */ #endif /* MINIMAL_INLINING */ an_expr_node_ptr add_object_lifetime_to_expr(an_expr_node_ptr expr, an_object_lifetime_ptr lifetime) /* Put an enk_object_lifetime node on top of the indicated expression and return a pointer to the new expression. */ { an_expr_node_ptr orig_expr = expr; expr = alloc_expr_node((an_expr_node_kind)enk_object_lifetime); expr->variant.object_lifetime.expr = orig_expr; copy_node_value_category(orig_expr, expr); /* expr->variant.object_lifetime.ptr is set by the bind call. */ expr->type = orig_expr->type; bind_object_lifetime(lifetime, iek_expr_node, (char *)expr); return expr; } /* add_object_lifetime_to_expr */ an_expr_node_ptr duplicate_default_arg_expr(an_expr_node_ptr expr) /* Copy a default argument expression and return a pointer to the copy. This routine is used to copy such expressions when function types pointing to default argument expressions are copied, but not to copy them when they are added implicitly to calls. The difference between the two is in the handling of object lifetimes. The copy is made in the file scope even if the current IL memory region is a function scope memory region. */ { an_object_lifetime_ptr lifetime = NULL, saved_curr_object_lifetime = NULL; a_memory_region_number region_to_switch_back_to; switch_to_file_scope_region(®ion_to_switch_back_to); if (expr->kind == (an_expr_node_kind)enk_object_lifetime) { /* Make a copy of the object lifetime and push it onto the object lifetime stack. Dynamic inits copied will be put into the new lifetime. */ saved_curr_object_lifetime = curr_object_lifetime; /* Clear the object lifetime stack before pushing this lifetime. */ curr_object_lifetime = il_header.primary_scope->lifetime; push_object_lifetime(iek_none, (char *)NULL, expr->variant.object_lifetime.ptr->kind); lifetime = curr_object_lifetime; expr = expr->variant.object_lifetime.expr; } /* if */ /* Copy the expression. */ expr = copy_expr_tree(expr, CE_NO_OPTIONS); if (lifetime != NULL) { /* Put an enk_object_lifetime node on the copy. */ expr = add_object_lifetime_to_expr(expr, lifetime); curr_object_lifetime = saved_curr_object_lifetime; } /* if */ switch_back_to_original_region(region_to_switch_back_to); return expr; } /* duplicate_default_arg_expr */ an_expr_node_ptr copy_default_arg_expr( a_routine_ptr rout, a_param_type_ptr ptp, a_boolean inside_conditional_expression, a_boolean potentially_evaluated, a_boolean evaluated) /* Copy the default argument expression from ptp, which is a parameter of rout, and return a pointer to the copy. This routine is used to copy such expressions when they are added implicitly to calls, but not to copy them when function types pointing to default argument expressions are copied. The difference between the two is in the handling of object lifetimes. In addition, a flag is set to identify this as a "generated" default argument expression. inside_conditional_expression is TRUE if the default argument expression copy will be inside a conditional part of an expression. potentially_evaluated is TRUE if the expression is potentially evaluated. evaluated is TRUE if the expression is evaluated. */ { an_expr_copy_options_set options = CE_NO_OPTIONS; an_expr_node_ptr expr; if (ptp->has_unevaluated_template_default) { /* This is a parameter of a function template, or a member function of a template class, and the default value has not yet been instantiated. Instantiate it now. */ /* First check for some problems that may arise because of prior errors. */ if (rout == NULL) { expect_error_str("copy_default_arg_expr: rout NULL, no error"); } else if (symbol_for(rout)->variant.routine.instance_ptr == NULL) { expect_error_str("copy_default_arg_expr: missing template instance ptr"); } else { instantiate_default_argument(symbol_for(rout), ptp); } /* if */ } /* if */ /* Watch out for cases where a default argument is followed by a non-default argument. An error will have been issued at the point of declaration of the function, but the problem could not be corrected there because the function type may have come from a typedef (i.e., it might be shared). */ if (ptp->default_arg_expr == NULL) { expr = error_node(); } else { expr = ptp->default_arg_expr; if (expr->kind == (an_expr_node_kind)enk_object_lifetime) { /* The top node is an enk_object_lifetime. The lifetime is not copied. Instead, copies of dynamic inits associated with that lifetime will be bound into the curr_object_lifetime. */ expr = expr->variant.object_lifetime.expr; } /* if */ if (potentially_evaluated) { options = CE_COPYING_EVALUATED_DEFAULT_ARG_EXPR; if (inside_conditional_expression) { /* The copy will be inside a conditional part of an expression. */ options |= CE_INSIDE_CONDITIONAL_EXPRESSION; } /* if */ } /* if */ if (!evaluated) { options = CE_COPY_NOT_EVALUATED; } /* if */ expr = copy_expr_tree(expr, options); if (gpp_mode && gnu_version >= 30400 && gnu_version < 40000) { /* g++ 3.4 leaves the final conversion to be done at the point of use for member functions and friends. */ expr = prep_default_arg_expr(expr, ptp, evaluated); } /* if */ } /* if */ expr->generated_default_arg = TRUE; return expr; } /* copy_default_arg_expr */ an_expr_node_ptr copy_default_arg_expr_list( a_routine_ptr rout, a_param_type_ptr ptp, a_boolean inside_conditional_expression, a_boolean potentially_evaluated, a_boolean evaluated) /* Make an expression list containing copies of the default argument expressions for the parameter indicated by ptp, which is a parameter of rout, and all parameters following that. If ptp is non-NULL, it must point to a parameter with a default argument expression (or a parameter pack or parameter array, in which case no copies are made). inside_conditional_expression is TRUE if the default argument expression copies will be inside a conditional part of an expression. potentially_evaluated is TRUE if the expression is potentially evaluated. evaluated is TRUE if the expression is evaluated. */ { an_expr_node_ptr first_node = NULL, last_node = NULL, arg_node; if (ptp == NULL) { /* Nothing to copy. */ } else if (ptp->is_parameter_pack) { /* Assume this parameter will be instantiated as an empty pack. */ } else if (ptp->is_pack_element && !ptp->has_default_arg) { /* This is possible in error cases. */ check_assertion(is_error_type(ptp->type)); expect_error(); #if MICROSOFT_EXTENSIONS_ALLOWED } else if (ptp->is_cli_param_array) { /* Assume this parameter will correspond to a zero-length C++/CLI parameter array. */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else { #if CHECKING if (ptp->default_arg_expr == NULL && !ptp->has_unevaluated_template_default && !rout->is_prototype_instantiation) { internal_error("copy_default_arg_expr_list: param has no default arg"); } /* if */ #endif /* CHECKING */ /* Copy the default argument expressions. */ do { arg_node = copy_default_arg_expr(rout, ptp, inside_conditional_expression, potentially_evaluated, evaluated); if (first_node == NULL) { first_node = arg_node; } else { check_assertion(last_node != NULL); last_node->next = arg_node; } /* if */ last_node = arg_node; } while ((ptp = ptp->next) != NULL); } /* if */ return first_node; } /* copy_default_arg_expr_list */ #if MICROSOFT_EXTENSIONS_ALLOWED static void examine_expr_for_gc_lvalue( an_expr_node_ptr node, an_expr_or_stmt_traversal_block_ptr tblock) /* Called from the expression traversal routines to process an expression as part of finding out whether it is a gc-lvalue. The expression passed in is an addressing expression, meaning either a glvalue that identifies an object or a prvalue that is a pointer or handle to an object. */ { a_boolean determined_result = FALSE; a_type_ptr type = node->type; if (is_glvalue_node(node)) { /* The expression passed in is a glvalue for an object. */ if (is_ref_class_type(type)) { /* A ref class is always on the managed heap. */ tblock->result = TRUE; determined_result = TRUE; } else if (is_standard_class_type(type) || is_interior_ptr_type(type) || is_pin_ptr_type(type)) { /* Native classes, interior_ptrs, and pin_ptrs are never on the managed heap. */ tblock->result = FALSE; determined_result = TRUE; } else { switch (node->kind) { case enk_variable: /* Static data members of managed classes are on the managed heap (they're in a data block associated with the class that's allocated before the first use of an instance of the class). */ { a_variable_ptr var = node_variable(node); if (var->source_corresp.is_class_member) { a_type_ptr parent_class = parent_class_of(var); if (is_immediate_managed_class_type(parent_class)) { tblock->result = TRUE; determined_result = TRUE; } /* if */ } /* if */ } break; case enk_operation: /* Operator. */ { an_expr_operator_kind op = node->variant.operation.kind; an_expr_node_ptr operand1 = node->variant.operation.operands; if (op == (an_expr_operator_kind)eok_ref_indirect) { /* Reference indirection. If it's indirect through a tracking reference, the lvalue may be on the managed heap. Note that if the underlying type of the reference is a type that can't be on the managed heap (e.g., a native class type), that will have been handled above. */ if (is_tracking_reference_type(operand1->type)) { tblock->result = TRUE; determined_result = TRUE; } /* if */ } else if (op == (an_expr_operator_kind)eok_unbox || op == (an_expr_operator_kind)eok_unbox_lvalue) { /* Unbox returns a gc-lvalue for the value contained within the managed heap object pointed to by its handle operand. */ tblock->result = TRUE; determined_result = TRUE; } /* if */ } break; default: break; } /* switch */ } /* if */ } else { /* The expression passed in is a prvalue pointer or handle. */ if (is_handle_type(type) || is_interior_ptr_type(type)) { /* A handle always points to the managed heap. */ /* An interior_ptr might point to the managed heap. */ tblock->result = TRUE; determined_result = TRUE; } else { /* Other pointers cannot point to the managed heap (or if they do they are pinned pointers or copies thereof and we don't need to worry about them). */ check_assertion(is_pointer_type(type)); tblock->result = FALSE; determined_result = TRUE; } /* if */ } /* if */ if (!tblock->terminate) { if (determined_result) { tblock->suppress_subtree_walk = TRUE; if (tblock->result) { /* A TRUE result is sticky. */ tblock->terminate = TRUE; } /* if */ } /* if */ } /* if */ } /* examine_expr_for_gc_lvalue */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ a_boolean is_gc_lvalue_expr(ARG_UNUSED an_expr_node_ptr expr) /* Return TRUE if the given expression is a gc-lvalue for C++/CLI. Roughly, that means it is an lvalue that is or may be on the managed heap. The important principle is that we never want to allow something that might point to the managed heap to be captured as a normal pointer that might thereafter get stale if the garbage collector moves the underlying object. */ { a_boolean is_gc_lvalue = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED if (cppcli_enabled && is_glvalue_node(expr)) { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = examine_expr_for_gc_lvalue; tblock.follow_addressing_path = TRUE; traverse_expr(expr, &tblock); is_gc_lvalue = tblock.result; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ return is_gc_lvalue; } /* is_gc_lvalue_expr */ static void examine_expr_for_cannot_be_null( an_expr_node_ptr expr, an_expr_or_stmt_traversal_block_ptr tblock) /* Called from the expression traversal routines to process an expression as part of determining whether it cannot be NULL. The expression passed in is an addressing expression, meaning either a glvalue that identifies an object or a prvalue that is a pointer to an object. For a glvalue, the test means its address cannot be NULL. */ { a_boolean cannot_be = FALSE; /* Note that the safe result is FALSE; TRUE means we have information that guarantees that the expression is not NULL. */ if (is_glvalue_node(expr)) { /* The expression passed in is a glvalue for an object. */ if (expr->kind == (an_expr_node_kind)enk_variable) { /* The address of a variable is generally non-NULL, but watch out for extern variables affected by linker magic. */ cannot_be = variable_has_non_null_address(node_variable(expr)); } else if (expr->kind == (an_expr_node_kind)enk_routine) { /* The address of a routine is generally non-NULL, but watch out for extern routines affected by linker magic. */ cannot_be = routine_has_non_null_address(node_routine(expr)); } else if (is_operation_node(expr)) { an_expr_operator_kind op = expr->variant.operation.kind; an_expr_node_ptr operand = expr->variant.operation.operands; if (op == (an_expr_operator_kind)eok_dot_field || op == (an_expr_operator_kind)eok_points_to_field) { /* The address of a field reference can't be NULL in a legal program, but watch out for ((struct s *)0)->i. */ if (node_field(operand->next)->offset != 0) { /* The field offset is non-zero, so even if the pointer is zero the address will be non-zero. */ cannot_be = TRUE; } else { /* The field offset is zero, so keep going and check the class object address. */ } /* if */ } else if (op == (an_expr_operator_kind)eok_ref_indirect && assume_references_cannot_be_null) { /* Indirection through a reference can't produce a zero address according to the C++ standard. */ cannot_be = TRUE; } /* if */ } /* if */ } else { /* The expression passed in is a pointer to an object. */ if (is_constant_node(expr)) { a_constant_ptr con = node_constant(expr); if (con->kind == (a_constant_repr_kind)ck_integer) { /* An integer constant (cast to a pointer type in this case) is non-NULL if it's non-zero. */ cannot_be = !eqlit_integer_constant(con, (a_host_large_integer)0); } else if (con->kind == (a_constant_repr_kind)ck_address) { /* An address constant cannot be NULL as long as its value is known at compile time. */ cannot_be = constant_bool_value_known_at_compile_time(con); } /* if */ } else if (expr->kind == (an_expr_node_kind)enk_variable) { a_variable_ptr var = node_variable(expr); if (innermost_function_scope != NULL && innermost_function_scope->variant.routine.this_param_variable == var) { /* If the expression is the "this" variable for the current function, it cannot be null. */ cannot_be = TRUE; } /* if */ } else if (expr->kind == (an_expr_node_kind)enk_address_of_ellipsis) { /* "&..." cannot be NULL. */ cannot_be = TRUE; } /* if */ } /* if */ if (!tblock->terminate && cannot_be) { tblock->result = cannot_be; tblock->terminate = TRUE; } /* if */ } /* examine_expr_for_cannot_be_null */ a_boolean cannot_be_null(an_expr_node_ptr expr) /* For expressions with rvalue pointer type, returns TRUE if the value of the expression cannot be NULL (0). For lvalue expressions, returns TRUE if the address of the expression cannot be NULL. This routine is used for an optimization, and a diagnostic, so it doesn't have to be perfect. The safe return value is FALSE. */ { an_expr_or_stmt_traversal_block tblock; check_assertion(is_glvalue_node(expr) || is_pointer_type(expr->type)); clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = examine_expr_for_cannot_be_null; tblock.follow_addressing_path = TRUE; traverse_expr(expr, &tblock); return tblock.result; } /* cannot_be_null */ an_expr_node_ptr var_lvalue_expr(a_variable_ptr var) /* Build an expression node that represents an lvalue for var and return a pointer to it. Note that this routine does not do anything special for variables with reference type. */ { an_expr_node_ptr node; node = alloc_expr_node((an_expr_node_kind)enk_variable); node->type = var->type; node->is_lvalue = TRUE; node_variable(node) = var; return node; } /* var_lvalue_expr */ an_expr_node_ptr var_rvalue_expr(a_variable_ptr var) /* Build an expression node that represents an rvalue for var and return a pointer to it. Note that this routine does not do anything special for variables with reference type. */ { an_expr_node_ptr node; node = alloc_expr_node((an_expr_node_kind)enk_variable); /* Drop any type qualifiers on the variable type as appropriate. */ node->type = prvalue_type(var->type); node_variable(node) = var; return node; } /* var_rvalue_expr */ an_expr_node_ptr var_addr_expr(a_variable_ptr var) /* Build an expression node that represents the address of var as an rvalue and return a pointer to it. Note that this routine does not do anything special for variables with reference type. This is intended for generated code and not for implementation of "&" in the source code. */ { an_expr_node_ptr node = var_lvalue_expr(var); #if LOWER_VARIABLE_LENGTH_ARRAYS if (il_lowering_underway && vla_enabled && is_vla_type(var->type)) { /* Add a pointer-to to the type of the VLA enk_variable node. VLA variables are eventually lowered to 'pointer-to underlying-element-type'; expressions that refer to them are modified in two steps, first by adding a 'pointer-to' (as we're doing here), then in lower_vla_types (when the expression type will be changed from 'pointer to array [] of T' to 'pointer to T'). */ node->type = make_pointer_type(node->type); } /* if */ #endif /* LOWER_VARIABLE_LENGTH_ARRAYS */ node = add_address_of_to_node(node); return node; } /* var_addr_expr */ an_expr_node_ptr function_lvalue_expr(a_routine_ptr rout) /* Build an expression node that represents an lvalue for the function rout and return a pointer to it. */ { an_expr_node_ptr node; if (rout->has_deducible_return_type && !rout->has_deduced_return_type) { finalize_deduced_return_type(rout, &error_position); } /* if */ node = alloc_expr_node((an_expr_node_kind)enk_routine); node->type = rout->type; node->variant.routine.ptr = rout; node->is_lvalue = TRUE; return node; } /* function_lvalue_expr */ an_expr_node_ptr function_rvalue_expr(a_routine_ptr rout) /* Build an expression node that represents the address of the function rout and return a pointer to it. This is suitable for use in calling the function, but not for the "&" operator applied to the function, because the address_taken flag is not set. */ { an_expr_node_ptr node; if (rout->has_deducible_return_type && !rout->has_deduced_return_type) { finalize_deduced_return_type(rout, &error_position); } /* if */ node = alloc_expr_node((an_expr_node_kind)enk_routine); node->type = make_pointer_type(rout->type); node->variant.routine.ptr = rout; return node; } /* function_rvalue_expr */ an_expr_node_ptr function_addr_expr(a_routine_ptr rout) /* Build an expression node that represents the address of the function rout and return a pointer to it. The address_taken flag is set in the routine entry, so this is not intended to be used to get an operand for the function in order to call it. */ { an_expr_node_ptr node; node = alloc_expr_node((an_expr_node_kind)enk_routine); node->type = make_pointer_type(rout->type); node->variant.routine.ptr = rout; rout->address_taken = TRUE; return node; } /* function_addr_expr */ an_expr_node_ptr rvalue_expr_for_lvalue(an_expr_node_ptr expr) /* Perform any changes needed to convert expr from a glvalue to a prvalue. Return the rvalue expression. This routine does not handle replacing constant-valued variables by their constant values -- see conv_glvalue_expr_to_prvalue. */ { expr = conv_glvalue_expr_to_prvalue(expr, (a_boolean *)NULL, (a_constant **)NULL, (a_source_position *)NULL); return expr; } /* rvalue_expr_for_lvalue */ an_expr_node_ptr xvalue_expr_for_lvalue(an_expr_node_ptr expr) /* Perform any changes needed to convert expr from an lvalue to an xvalue. Return the xvalue expression. This is designed for simple compiler-generated expressions and might have to be enhanced if it had to deal with complex expressions. */ { check_assertion(expr->is_lvalue); expr->is_lvalue = FALSE; expr->is_xvalue = TRUE; return expr; } /* xvalue_expr_for_lvalue */ an_expr_node_ptr add_indirection_to_node(an_expr_node_ptr node) /* Add an indirection on top of the given node, and return a pointer to the new expression. This is intended for generated code and not for implementation of "*" in the source code. The returned node is designated an lvalue. */ { if (!is_error_node(node)) { if (is_operation_node(node) && (node->compiler_generated #if DO_IL_LOWERING || il_lowering_underway #endif /* DO_IL_LOWERING */ ) && node_operator_is(node, eok_address_of)) { /* Cancel "*" and "&". */ node = node->variant.operation.operands; check_assertion(node->is_lvalue); } else { a_type_ptr new_type; check_assertion(!node->is_lvalue); if (is_pointer_type(node->type) #if DO_IL_LOWERING || (is_reference_type(node->type) && il_lowering_underway) #endif /* DO_IL_LOWERING */ #if MICROSOFT_EXTENSIONS_ALLOWED || (cli_or_cx_enabled && is_handle_type(node->type)) #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ ) { new_type = type_pointed_to(node->type); } else if (!C_mode() && is_template_param_type(node->type)) { new_type = type_of_unknown_templ_param_nontype; } else { check_assertion(is_error_type(node->type)); new_type = error_type(); } /* if */ node->next = NULL; node = make_lvalue_operator_node((an_expr_operator_kind)eok_indirect, new_type, node); node->compiler_generated = TRUE; } /* if */ } /* if */ return node; } /* add_indirection_to_node */ an_expr_node_ptr add_ref_indirection_to_node(an_expr_node_ptr node) /* Add an implicit reference indirection on top of the given node, and return a pointer to the new expression. The returned node is designated an lvalue. */ { if (!is_error_node(node)) { a_type_ptr new_type; check_assertion(!node->is_lvalue); if (is_any_reference_type(node->type)) { new_type = type_pointed_to(node->type); } else if (!C_mode() && is_template_param_type(node->type)) { new_type = type_of_unknown_templ_param_nontype; } else { check_assertion(is_error_type(node->type)); new_type = error_type(); } /* if */ node->next = NULL; node = make_lvalue_operator_node((an_expr_operator_kind)eok_ref_indirect, new_type, node); node->compiler_generated = TRUE; } /* if */ return node; } /* add_ref_indirection_to_node */ static void set_address_taken_on_target_of_addressing_op( an_expr_node_ptr expr, ARG_UNUSED an_expr_or_stmt_traversal_block_ptr tblock) /* Called from set_address_taken_for_variable_or_routine_expr via traverse_expr. If expr is an lvalue enk_variable node or an enk_routine node, set the address_taken flag on the variable or routine. Note that the traversal isn't terminated once a flag is set (allows for something like &(i ? j : k) ). */ { if (is_variable_node(expr)) { if (expr->is_lvalue) { /* We only want lvalues -- i.e., "x.i" and not "p->i". */ set_variable_address_taken(node_variable(expr)); } /* if */ } else if (is_routine_node(expr)) { node_routine(expr)->address_taken = TRUE; } /* if */ } /* set_address_taken_on_target_of_addressing_op */ void set_address_taken_for_variable_or_routine_expr(an_expr_node_ptr node) /* Traverse the expression to find the ultimate variable or routine node(s) whose address is taken and set the address_taken flag on these nodes. */ { an_expr_or_stmt_traversal_block tblock; if (node->is_lvalue) { clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = set_address_taken_on_target_of_addressing_op; tblock.follow_addressing_path = TRUE; traverse_expr(node, &tblock); } else { /* Address isn't taken on cases that aren't lvalues (e.g., rvalue arrays). */ } /* if */ } /* set_address_taken_for_variable_or_routine_expr */ a_type_ptr type_of_address_of(an_expr_node_ptr node) /* Return the type of the address-of operator "&" applied to the indicated expression. Normally, that is pointer-to its type, but in C++/CLI it can be interior_ptr-to the type. Note that it is never handle-to the type. See add_right_pointer_type_to_this for a variant of this function. */ { /* If the lvalue is a C++/CLI gc-lvalue, the "&" operator's type is an interior_ptr. */ a_type_ptr addr_type = #if MICROSOFT_EXTENSIONS_ALLOWED is_gc_lvalue_expr(node) ? make_interior_ptr_type(node->type) : #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ make_pointer_type(node->type); return addr_type; } /* type_of_address_of */ an_expr_node_ptr add_address_of_to_node(an_expr_node_ptr node) /* Add an eok_address_of operation on top of the given node (an lvalue), and return a pointer to the new expression. This is intended for generated code and not for implementation of "&" in the source code. The returned node is a prvalue. */ { if (!is_error_node(node)) { check_assertion(node->is_lvalue); if (is_operation_node(node) && (node->compiler_generated #if DO_IL_LOWERING || il_lowering_underway #endif /* DO_IL_LOWERING */ ) && node_operator_is(node, eok_indirect)) { /* Cancel "&" and "*". */ node = node->variant.operation.operands; check_assertion(!node->is_lvalue); } else { /* Set the address_taken flag for variables and routines. */ a_type_ptr addr_type = type_of_address_of(node); set_address_taken_for_variable_or_routine_expr(node); node->next = NULL; node = make_operator_node((an_expr_operator_kind)eok_address_of, addr_type, node); node->compiler_generated = TRUE; } /* if */ } /* if */ return node; } /* add_address_of_to_node */ an_expr_node_ptr glvalue_from_class_prvalue_node(an_expr_node_ptr node, a_boolean is_xvalue) /* Return a node that is the given node converted to a glvalue. This is done by adding an enk_temp_init node on top of the given node, unless it's already an enk_temp_init node, an enk_lambda node, or a chain of eok_comma and eok_dot_field nodes ending in an enk_temp_init or enk_lambda node. Return an xvalue if is_xvalue is TRUE or an lvalue otherwise. */ { an_expr_node_ptr chain, chain_end = node; /* Skip any eok_comma, eok_parens, and eok_dot_field nodes. */ while (is_operation_node(chain_end)) { if (node_operator_is(chain_end, eok_comma)) { chain_end = chain_end->variant.operation.operands->next; } else if (node_operator_is(chain_end, eok_parens) || node_operator_is(chain_end, eok_dot_field)) { chain_end = chain_end->variant.operation.operands; } else { break; } /* if */ } /* while */ /* Add an enk_temp_init node, unless we're already dealing with a node representing a temporary. */ if (chain_end->kind != (an_expr_node_kind)enk_temp_init && chain_end->kind != (an_expr_node_kind)enk_lambda) { a_dynamic_init_ptr dip = alloc_expr_dynamic_init( (a_dynamic_init_kind)dik_expression); an_expr_node_ptr orig_node = node; dip->variant.expression = node; node = alloc_temp_init_node(node->type, dip, /*is_lvalue=*/TRUE, /*is_explicit_cast=*/FALSE); node->is_lvalue = FALSE; node->position = orig_node->position; chain_end = node; } /* if */ for (chain = node; chain != chain_end;) { if (is_xvalue) { chain->is_xvalue = TRUE; } else { chain->is_lvalue = TRUE; } /* if */ if (node_operator_is(chain, eok_comma)) { chain->variant.operation .returns_lvalue_instead_of_usual_rvalue = TRUE; chain = chain->variant.operation.operands->next; } else if (node_operator_is(chain, eok_parens) || node_operator_is(chain, eok_dot_field)) { chain = chain->variant.operation.operands; } else { break; } /* if */ } /* for */ if (is_xvalue) { chain->is_xvalue = TRUE; } else { chain->is_lvalue = TRUE; } /* if */ return node; } /* glvalue_from_class_prvalue_node */ an_expr_node_ptr add_reference_to_to_node(an_expr_node_ptr node) /* Add an eok_reference_to operation on top of the given node (a glvalue or class prvalue), and return a pointer to the new expression. This is for reference binding, and does not correspond directly to any operator in the source code. The returned node is a prvalue. */ { if (!is_error_node(node)) { a_type_ptr ref_type; if (is_glvalue_node(node)) { /* Set the address_taken flag for variables and routines. */ set_address_taken_for_variable_or_routine_expr(node); /* If the entity is a gc-lvalue, the reference created is a tracking reference. */ ref_type = #if MICROSOFT_EXTENSIONS_ALLOWED is_gc_lvalue_expr(node) ? make_tracking_reference_type(node->type) : #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ make_reference_type(node->type); } else { /* For the prvalue case, the operand should be a class. */ check_assertion(is_class_struct_union_type(node->type) || is_template_param_type(node->type) || is_error_type(node->type)); /* Turn the node into an xvalue node. */ node = glvalue_from_class_prvalue_node(node, /*is_xvalue=*/TRUE); /* If the entity is an rvalue of a ref class type, the reference created is a tracking reference. */ ref_type = #if MICROSOFT_EXTENSIONS_ALLOWED is_ref_class_type(node->type) ? make_tracking_reference_type(node->type) : #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ make_reference_type(node->type); } /* if */ node->next = NULL; node = make_operator_node((an_expr_operator_kind)eok_reference_to, ref_type, node); node->compiler_generated = TRUE; } /* if */ return node; } /* add_reference_to_to_node */ an_expr_node_ptr this_param_value_expr(void) /* Return an expression for the value of the "this" parameter of the current function (which must have such a parameter). This routine is used only in C++ mode. */ { an_expr_node_ptr expr; a_variable_ptr this_param_var; check_assertion(innermost_function_scope != NULL); this_param_var = this_variable_for_func_scope(innermost_function_scope); #if CHECKING if (this_param_var == NULL) { internal_error("this_param_value_expr: no this param"); } /* if */ #endif /* CHECKING */ expr = var_rvalue_expr(this_param_var); expr->position = error_position; return expr; } /* this_param_value_expr */ an_expr_node_ptr field_lvalue_selection_expr(an_expr_node_ptr node, a_field_ptr field) /* Make an expression for an lvalue reference to field "field" of "node" and return a pointer to it. "node" is either a class lvalue, in which case the operator is ".", or a pointer to class, in which case the operator is "->". This is intended for generated code and not for the source "." or "->" operators. Note that this does NOT add extra intermediate selections for anonymous unions (either standard or nonstandard). Within the front end proper, use fe_field_lvalue_selection_expr instead. */ { an_expr_operator_kind op; an_expr_node_ptr field_node; a_type_ptr selection_type, class_type; a_type_qualifier_set qualifiers; #if DO_IL_LOWERING /* See if we can optimize some of the operations added during lowering. */ if (il_lowering_underway && is_operation_node(node) && node->compiler_generated) { an_expr_node_ptr operand = node->variant.operation.operands; if ((node_operator_is(node, eok_address_of) && is_class_struct_union_type(operand->type)) || (node_operator_is(node, eok_indirect) && is_pointer_type(operand->type) && is_class_struct_union_type(type_pointed_to(operand->type)))) { /* Create "x.y" instead of "(&x)->y", or "x->y" instead of "(*x).y". */ node = operand; } /* if */ } /* if */ #endif /* DO_IL_LOWERING */ if (is_pointer_type(node->type)) { /* Use a "->" operator. */ op = (an_expr_operator_kind)eok_points_to_field; class_type = type_pointed_to(node->type); } else { /* Use a "." operator. */ op = (an_expr_operator_kind)eok_dot_field; class_type = node->type; } /* if */ /* Make the expression node for the field. */ field_node = alloc_expr_node((an_expr_node_kind)enk_field); field_node->type = field->type; node_field(field_node) = field; node->next = field_node; /* The selected field has all the type qualifiers of both the field and the selecting pointer. */ qualifiers = get_type_qualifiers(class_type); selection_type = make_field_selection_type(field, qualifiers); /* Make the field selection node. */ node = make_lvalue_operator_node(op, selection_type, node); node->compiler_generated = TRUE; return node; } /* field_lvalue_selection_expr */ an_expr_node_ptr field_rvalue_selection_expr(an_expr_node_ptr node, a_field_ptr field) /* Make an expression for an rvalue reference to field "field" of "node" and return a pointer to it. "node" is a class rvalue, and the operator used is ".". This is intended for generated code, and not for the source "." operator. Note that this does NOT add extra intermediate selections for anonymous unions (either standard or nonstandard). */ { /* Make the expression node for an lvalue reference. */ node = field_lvalue_selection_expr(node, field); /* Turn it into an rvalue. */ node->is_lvalue = FALSE; node->type = prvalue_type(node->type); return node; } /* field_rvalue_selection_expr */ void adjust_anonymous_union_field_selection(an_expr_node_ptr node, a_field_ptr au_field) /* node is a field selection of some kind that selects an anonymous union field out of an anonymous parent object (or a similar C case allowed as an extension). au_field is the field for the anonymous parent object. Insert an additional selection on the first operand, e.g., change "x.y" to "x.au_field.y". If a multi-level anonymous union is involved, this routine handles only one level; the caller must loop to handle the rest. */ { an_expr_node_ptr op1, op2, new_op1, au_field_node; an_expr_operator_kind op; a_type_ptr new_selection_type; /* The new selection replaces the first operand of the original selection, e.g., p->x becomes (p->au_field).x. The operator in the new selection is the original operator (it's based on whether the original first operand is a pointer or not) and the top field selection operator becomes "." in all cases (its first operand is always a class, the value returned from the introduced field selection). */ check_assertion(is_operation_node(node)); op = node->variant.operation.kind; op1 = node->variant.operation.operands; op2 = op1->next; /* Determine the underlying class type for the first operand so we can know what qualifiers to add to the intermediate result. */ if (op == eok_dot_field) { new_selection_type = op1->type; } else { check_assertion(op == eok_points_to_field); if (is_pointer_type(op1->type)) { new_selection_type = type_pointed_to(op1->type); } else { new_selection_type = error_type(); } /* if */ } /* if */ #if DO_IL_LOWERING if (il_lowering_underway && is_a_unique_subobject_type(skip_typerefs(new_selection_type))) { /* If we're selecting a field from a subobject type, make sure we use the correct field in the subobject (au_field currently specifies the field in the complete type). */ au_field = corresponding_subobject_au_field( skip_typerefs(new_selection_type), au_field); } /* if */ #endif /* DO_IL_LOWERING */ new_selection_type = type_plus_qualifiers_from_second_type(au_field->type, new_selection_type); au_field_node = alloc_expr_node(enk_field); au_field_node->type = au_field->type; node_field(au_field_node) = au_field; op1->next = au_field_node; new_op1 = make_operator_node(node->variant.operation.kind, new_selection_type, op1); /* Assign the appropriate value category to the new node. */ if (op == eok_dot_field) { copy_node_value_category(op1, new_op1); } else { new_op1->is_lvalue = TRUE; } /* if */ new_op1->compiler_generated = TRUE; /* Attach the new selection to the original selection. */ new_op1->next = op2; node->variant.operation.operands = new_op1; node->variant.operation.kind = eok_dot_field; } /* adjust_anonymous_union_field_selection */ void adjust_nonstandard_anonymous_object_field_references( an_expr_node_ptr node, a_symbol_ptr field_sym, a_boolean std_also) /* node points to an expression for a field selection of the field field_sym. If field_sym is a member of a nonstandard anonymous parent (rather than a standard C++ anonymous union, or no anonymous parent at all), insert the elided field selections. If std_also is TRUE, also do the insertions for standard anonymous unions. The insertions, if any, are done in place; the expression address does not change. This routine can be called only when IL entries still point back to the associated symbols. */ { a_symbol_ptr anon_parent_sym = field_sym; a_field_ptr field; /* Loop for multiple levels of anonymous parent objects. */ for (;;) { check_assertion(anon_parent_sym->kind == (a_symbol_kind)sk_field); field = anon_parent_sym->variant.field.ptr; #if DO_IL_LOWERING if (il_lowering_underway) { /* When secondary translation units are involved, the symbol we got may be from a secondary translation unit, so make sure we get the field in the primary IL. */ field = (a_field_ptr)canonical_il_entry_of(field); } /* if */ #endif /* DO_IL_LOWERING */ anon_parent_sym = anon_parent_sym->variant.field.anonymous_parent_object; /* Stop if there's no anonymous parent, meaning we've handled all the levels of anonymous parents. */ if (anon_parent_sym == NULL) break; /* Stop if we've worked up to a top-level (variable) anonymous union. */ if (anon_parent_sym->kind == (a_symbol_kind)sk_variable) break; check_assertion(anon_parent_sym->kind == (a_symbol_kind)sk_field); /* In C++, skip a standard anonymous union, because those don't get handled here. But keep looping because there might be more nonstandard cases further out. */ /* If the std_also flag is set, process standard anonymous unions too. */ if (!std_also && !C_mode()) { /* C++. See if this is an anonymous union case. */ a_type_ptr field_class = parent_class_of(field); a_class_type_supplement_ptr ctsp = field_class->variant.class_struct_union.extra_info; /* Skip this level if the field is from a standard anonymous union. */ if (ctsp->anonymous_union_kind == (an_anonymous_union_kind)auk_field) { continue; } /* if */ } /* if */ field = anon_parent_sym->variant.field.ptr; #if DO_IL_LOWERING if (il_lowering_underway) { /* When secondary translation units are involved, the symbol we got may be from a secondary translation unit, so make sure we get the field in the primary IL. */ field = (a_field_ptr)canonical_il_entry_of(field); } /* if */ #endif /* DO_IL_LOWERING */ /* Rewrite the field selection to add an implied selection. */ adjust_anonymous_union_field_selection(node, field); /* Loop to see if the rewritten first operand still refers to an anonymous union field (because there are several nested anonymous unions), and if so, rewrite it. */ node = node->variant.operation.operands; } /* for */ } /* adjust_nonstandard_anonymous_object_field_references */ an_expr_node_ptr fe_field_lvalue_selection_expr(an_expr_node_ptr node, a_field_ptr field) /* Make an expression for an lvalue reference to field "field" of "node" and return a pointer to it. This is intended for use in the front end proper. It inserts extra field selections for nonstandard anonymous unions, but not for standard anonymous unions (those are added by IL lowering). */ { node = field_lvalue_selection_expr(node, field); adjust_nonstandard_anonymous_object_field_references(node, symbol_for(field), /*std_also=*/FALSE); return node; } /* fe_field_lvalue_selection_expr */ an_expr_node_ptr base_class_selection_expr(an_expr_node_ptr node, a_base_class_ptr bcp) /* Create an expression node that selects the base class indicated from the object pointed to by node, and return a pointer to it. The base class need not be an immediate base class. The result is an rvalue pointer. */ { if (!is_error_node(node) && !is_error_type(node->type)) { a_derivation_step_ptr dsp, tail; a_type_qualifier_set qualifiers; a_type_ptr tp; check_assertion(!is_glvalue_node(node) && is_pointer_type(node->type)); tp = type_pointed_to(node->type); qualifiers = get_type_qualifiers(tp); /* Add a base class cast for each step in the derivation. */ tail = bcp->derivation->path_tail; for (dsp = cast_derivation_path_of(bcp); dsp != tail->next; dsp = dsp->next) { tp = make_pointer_type(make_qualified_type(dsp->base_class->type, qualifiers)); node = make_operator_node((an_expr_operator_kind)eok_base_class_cast, tp, node); node->compiler_generated = TRUE; } /* for */ } /* if */ return node; } /* base_class_selection_expr */ an_expr_node_ptr base_class_rvalue_expr(an_expr_node_ptr node, a_base_class_ptr bcp) /* Create and return an expression node that selects the base class indicated from the rvalue represented by node. The base class need not be an immediate base class. The result is a class rvalue. */ { if (!is_error_node(node) && !is_error_type(node->type)) { a_derivation_step_ptr dsp; a_type_qualifier_set qualifiers; a_type_ptr tp; check_assertion(!is_glvalue_node(node)); tp = node->type; qualifiers = get_type_qualifiers(tp); /* Add a base class cast for each step in the derivation. */ for (dsp = cast_derivation_path_of(bcp); dsp != NULL; dsp = dsp->next) { tp = make_qualified_type(dsp->base_class->type, qualifiers); node = make_operator_node((an_expr_operator_kind)eok_base_class_cast, tp, node); node->compiler_generated = TRUE; } /* for */ } /* if */ return node; } /* base_class_rvalue_expr */ static void examine_constant_for_side_effect( a_constant_ptr con, an_expr_or_stmt_traversal_block_ptr tblock) /* Examine the indicated constant for side effects; called by the expression traversal routines. Set tblock->result to TRUE if the constant has a side effect. Set tblock->suppress_warning to TRUE if a warning about the dynamic initialization doing nothing should be suppressed. */ { if (con->kind == (a_constant_repr_kind)ck_error) { /* An error constant could have been anything, including something with side effects. */ tblock->result = TRUE; tblock->terminate = TRUE; } else if (con->kind == (a_constant_repr_kind)ck_template_param) { switch (con->variant.template_param.kind) { case tpck_sizeof: case tpck_datasizeof: /* A sizeof doesn't have side effects, but more than that its expression is unevaluated and can't have side effects. */ if (vla_enabled && is_vla_type(con->variant.template_param.variant. templ_sizeof.type) && (con->variant.template_param.variant.templ_sizeof.expr == NULL #if PROTOTYPE_INSTANTIATIONS_IN_IL && !con->variant.template_param.local_expr_ref #endif /*PROTOTYPE_INSTANTIATIONS_IN_IL */ )) { /* However, sizeof a VLA type evaluates the bound expression. Only sizeof(type) can have side effects; for sizeof(expr), the VLA type was generated elsewhere. */ /* Assume side effects from the type. */ tblock->result = TRUE; tblock->terminate = TRUE; } else { tblock->suppress_subtree_walk = TRUE; } /* if */ break; case tpck_noexcept: case tpck_alignof: /* These don't evaluate their operands. */ tblock->suppress_subtree_walk = TRUE; break; default: break; } /* switch */ } /* if */ } /* examine_constant_for_side_effect */ static void examine_dynamic_init_for_side_effect( a_dynamic_init_ptr dip, an_expr_or_stmt_traversal_block_ptr tblock) /* Examine the indicated dynamic initialization for side effects; called by the expression traversal routines. Set tblock->result to TRUE if the dynamic initialization has a side effect. Set tblock->suppress_warning to TRUE if a warning about the dynamic initialization doing nothing should be suppressed. */ { a_boolean has_side_effects = FALSE; if (dip->destructor != NULL) { /* A destructor call causes side effects. */ has_side_effects = TRUE; } else if (is_dynamic_init_for_vla(dip)) { /* A VLA causes side effects (allocation and deallocation). */ has_side_effects = TRUE; } else { switch (dip->kind) { case dik_constant: if (dip->variant.constant.ptr->kind==(a_constant_repr_kind)ck_error) { /* An error constant could have been anything, including something with side effects. */ has_side_effects = TRUE; } /* if */ break; case dik_constructor: /* A constructor call causes side effects. */ has_side_effects = TRUE; break; default: /* Others have no side effects at this level. The subtree might still cause side effects. */ break; } /* switch */ } /* if */ if (has_side_effects) { /* The dynamic initialization has a side effect, even without considering its subtree. */ tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* examine_dynamic_init_for_side_effect */ #endif /* !STANDALONE_UTILITY_PROGRAM */ static void examine_expr_for_restrict_pointer( an_expr_node_ptr expr, an_expr_or_stmt_traversal_block_ptr tblock) /* Called from the expression traversal routines to process an expression as part of seeing whether an expression is a pointer with restrict semantics. */ { if (is_variable_node(expr) || expr->kind == (an_expr_node_kind)enk_param_ref) { a_type_ptr var_type = is_variable_node(expr) ? node_variable(expr)->type : expr->type; /* Ignore error types. */ if (is_pointer_type(var_type)) { if ((get_type_qualifiers(var_type) & TQ_RESTRICT) != 0) { /* This is a variable whose type is a restrict-qualified pointer. */ tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED && DECL_MODIFIERS_IN_USE } else if (microsoft_mode && is_call_node(expr)) { a_routine_ptr rp = routine_from_function_expr( expr->variant.operation.operands); if (rp != NULL && (rp->decl_modifiers & DM_RESTRICT) != 0 && is_pointer_type(expr->type)) { /* A function marked with __declspec(restrict) returns a pointer that's guaranteed to be unaliased. */ tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED && DECL_MODIFIERS_IN_USE */ } /* if */ } /* examine_expr_for_restrict_pointer */ a_boolean node_is_pointer_with_restrict_semantics(an_expr_node_ptr node) /* Return TRUE if the expression "node" is a pointer rvalue that should be treated with "restrict" semantics. "restrict" is a type qualifier introduced in C99 to control aliasing of pointers. See C99 6.7.3.1. */ { a_boolean is_restrict_pointer = FALSE; if (!is_glvalue_node(node) && is_pointer_type(node->type)) { /* Walk the expression's addressing parts to see whether it is based on a restrict pointer. */ an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = examine_expr_for_restrict_pointer; tblock.follow_addressing_path = TRUE; traverse_expr(node, &tblock); is_restrict_pointer = tblock.result; } /* if */ return is_restrict_pointer; } /* node_is_pointer_with_restrict_semantics */ #if !STANDALONE_UTILITY_PROGRAM a_boolean is_rvalueable_node(an_expr_node_ptr node) /* Return TRUE if the indicated node is one in which a glvalue-to-prvalue conversion ("load from memory") at the end can be indicated simply by clearing the is_lvalue or is_xvalue flag. The node can be a glvalue or prvalue: if it is a glvalue, then clearing the flag will add the load. If it is a prvalue, then the flag has already been cleared and the node already indicates the load. */ { a_boolean rvalueable = FALSE; /* Note that this routine is very similar to node_does_fetch and conv_prvalue_expr_to_lvalue. */ switch (node->kind) { case enk_error: case enk_variable: case enk_temp_init: case enk_routine: case enk_typeid: rvalueable = TRUE; break; case enk_constant: { a_constant_ptr cp = node_constant(node); if (constant_is(cp, ck_template_param)) { rvalueable = TRUE; } /* if */ } break; case enk_param_ref: /* An enk_param_ref is generally rvalueable, but not when it represents "this". */ if (node->variant.param_ref.param_num != 0) rvalueable = TRUE; break; case enk_lambda: /* A lambda expression can have is_lvalue TRUE in some rare cases, but it's not really rvalueable. The normal state is is_lvalue FALSE, which doesn't require that there be a load of the lambda value -- it's just created as an rvalue. */ rvalueable = FALSE; break; case enk_operation: if (node->variant.operation.returns_lvalue_instead_of_usual_rvalue) { /* Nodes with this flag set are not rvalueable. You can clear the returns_lvalue_instead_of_usual_rvalue flag and the is_lvalue flag to make the node an rvalue, but there would still be no "load" at this level. */ } else { an_expr_operator_kind op = node->variant.operation.kind; switch (op) { case eok_parens: rvalueable = is_rvalueable_node(node->variant.operation.operands); break; case eok_dot_field: case eok_pm_field: /* These are rvalueable if the first operand is a glvalue. If the first operand is a prvalue, there's no place we can load from; the prvalue has already been "loaded". However, the C++14 model is that a prvalue.field or prvalue.*pmd selection is an xvalue that can still be "loaded". */ rvalueable = selection_from_prvalue_is_xvalue || (is_glvalue_node(node->variant.operation.operands) || is_error_node(node->variant.operation.operands)); break; case eok_points_to_field: case eok_pm_points_to_field: case eok_subscript: case eok_vector_subscript: case eok_cli_subscript: case eok_indirect: case eok_ref_indirect: case eok_ref_cast: case eok_lvalue_adjust: case eok_ref_dynamic_cast: case eok_va_arg: #if C99_IL_EXTENSIONS_SUPPORTED case eok_real_part: case eok_imag_part: case eok_gnu_min: case eok_gnu_max: #endif /* C99_IL_EXTENSIONS_SUPPORTED */ case eok_question: case eok_assign: case eok_add_assign: case eok_subtract_assign: case eok_multiply_assign: case eok_divide_assign: case eok_remainder_assign: case eok_shiftl_assign: case eok_shiftr_assign: case eok_and_assign: case eok_or_assign: case eok_xor_assign: case eok_padd_assign: case eok_psubtract_assign: case eok_pre_incr: case eok_pre_decr: case eok_comma: case eok_dot_static: case eok_points_to_static: case eok_unbox_lvalue: case eok_call: case eok_dot_member_call: case eok_points_to_member_call: case eok_dot_pm_call: case eok_points_to_pm_call: case eok_lvalue: rvalueable = TRUE; break; case eok_base_class_cast: case eok_derived_class_cast: /* A base <-> derived conversion between pointers is an rvalue operation (and is therefore not rvalueable), but such a conversion can also apply to an lvalue of class type and in that case the result is rvalueable. */ rvalueable = is_class_struct_union_type(node->type) && is_glvalue_node(node->variant.operation.operands); break; case eok_lvalue_cast: /* Not rvalueable; when converted to an rvalue it gets rewritten as a normal cast. */ case eok_unbox: /* Not rvalueable: the version with is_lvalue FALSE does a fetch, but it's an inherent part of the operation, not an implicit lvalue-to-rvalue conversion at the end. */ default: break; } /* switch */ } /* if */ break; #if REPRESENT_C11_GENERIC_CONSTRUCT_IN_IL case enk_c11_generic: rvalueable = is_rvalueable_node(node->variant.c11_generic.result); break; #endif /* REPRESENT_C11_GENERIC_CONSTRUCT_IN_IL */ #if BUILTIN_FUNCTIONS_ENABLED case enk_builtin_choose_expr: { an_expr_node_ptr op1 = node->variant.builtin_choose_expr.operands; if (node->variant.builtin_choose_expr.choose_first) { rvalueable = is_rvalueable_node(op1->next); } else { rvalueable = is_rvalueable_node(op1->next->next); } /* if */ } break; #endif /* BUILTIN_FUNCTIONS_ENABLED */ default: break; } /* switch */ return rvalueable; } /* is_rvalueable_node */ a_boolean node_includes_glvalue_to_prvalue_conv(an_expr_node_ptr node) /* Return TRUE if node includes an implicit glvalue-to-prvalue conversion ("load from memory"). This is indicated by the is_lvalue and is_xvalue flags being FALSE in a node where the default setting would be for is_lvalue to be TRUE. */ { a_boolean result; node = skip_parens(node); if (is_glvalue_node(node)) { /* Not a prvalue at all. */ result = FALSE; } else if (node->was_lvalue_temp_initializer) { result = TRUE; } else if (!is_rvalueable_node(node)) { /* The node is intrinsically a prvalue, not a glvalue that was implicitly converted to a prvalue. */ if (node->orig_lvalue_type != NULL && is_operation_node(node) && node_operator_is(node, eok_cast)) { /* An eok_lvalue_cast node is rvalued by turning it into an eok_cast node. Such situations are recognized by the type of the original eok_lvalue_cast being recorded in the resulting eok_cast node. */ result = TRUE; } else if (node->orig_lvalue_type != NULL && is_constant_node(node)) { /* Constants that are the result of a glvalue-to-prvalue conversion have a non-NULL orig_lvalue_type field set. */ result = TRUE; } else { result = FALSE; } /* if */ } else { a_type_ptr tp = skip_typerefs(node->type); if (tp->kind == tk_void) { result = FALSE; } else if (is_operation_node(node)) { /* For comma and conditional operators, whether there is an actual glvalue-to-prvalue conversion depends on the underlying operands. */ if (node_operator_is(node, eok_comma)) { result = node_includes_glvalue_to_prvalue_conv( node->variant.operation.operands->next); } else if (node_operator_is(node, eok_question)) { result = node_includes_glvalue_to_prvalue_conv( node->variant.operation.operands->next) || node_includes_glvalue_to_prvalue_conv( node->variant.operation.operands->next->next); } else if (is_call_node(node)) { /* A call to a function returning a reference might have involved a glvalue-to-prvalue conversion. */ result = node->orig_lvalue_type != NULL; } else { result = TRUE; } /* if */ } else { result = TRUE; } /* if */ } /* if */ return result; } /* node_includes_glvalue_to_prvalue_conv */ static a_boolean node_does_fetch(an_expr_node_ptr node, a_type_ptr *p_fetched_type) /* Return TRUE if the indicated expression does a fetch from memory. If so, and if p_fetched_type is non-NULL, set *p_fetched_type to the type of the object fetched (a glvalue type, with cv-qualifiers intact). Only the top-level node is considered -- fetches in child nodes are not. */ { a_boolean does_fetch = FALSE; a_type_ptr fetched_type = NULL; /* Note that this routine is very similar to is_rvalueable_node and conv_prvalue_expr_to_lvalue. */ if (p_fetched_type != NULL) *p_fetched_type = NULL; node = skip_parens(node); /* Only prvalue expressions can fetch something from memory. */ if (!is_glvalue_node(node)) { switch (node->kind) { case enk_variable: /* An rvalue variable reference fetches the variable's value. */ does_fetch = TRUE; fetched_type = node_variable(node)->type; break; case enk_temp_init: /* An rvalue temp init fetches the value of the temporary. */ does_fetch = TRUE; fetched_type = node->type; break; case enk_lambda: /* An rvalue lambda (the normal case) doesn't fetch anything. The lambda value can be just created as an rvalue without storing it anywhere first. */ break; case enk_typeid: /* An rvalue typeid fetches the typeinfo object. */ does_fetch = TRUE; fetched_type = make_qualified_type(node->type, (a_type_qualifier_set)TQ_CONST); break; case enk_operation: { an_expr_operator_kind op = node->variant.operation.kind; an_expr_node_ptr op1 = node->variant.operation.operands; an_expr_node_ptr op2 = op1->next; a_type_ptr operand_type; op1 = skip_parens(op1); if (op2 != NULL) op2 = skip_parens(op2); /* Lvalue-returning operations shouldn't have is_lvalue FALSE. */ check_assertion(!node->variant.operation. returns_lvalue_instead_of_usual_rvalue); switch (op) { case eok_dot_field: operand_type = op1->type; goto process_field_selection; case eok_points_to_field: /* Watch out for errors and prototype instantiations. */ if (!is_pointer_type(op1->type)) break; operand_type = type_pointed_to(op1->type); process_field_selection: /* A field selection that returns an rvalue fetches the field as additionally qualified by the class type. Note that the code here differs from node_includes_glvalue_to_prvalue_conv in considering that rvalue.field is a fetch of the field even though there's no lvalue-to-rvalue conversion. That makes sense because this routine is used to check for side effects, and a selection of a volatile field from a non-volatile class object does cause a side effect. */ does_fetch = TRUE; check_assertion(op2 != NULL && op2->kind == (an_expr_node_kind)enk_field); fetched_type = make_field_selection_type(node_field(op2), get_type_qualifiers( operand_type)); break; case eok_pm_field: operand_type = op1->type; goto process_ptr_to_member_selection; case eok_pm_points_to_field: /* Watch out for errors and prototype instantiations. */ if (!is_pointer_type(op1->type)) break; operand_type = type_pointed_to(op1->type); process_ptr_to_member_selection: /* .* and ->*. The second operand is a pointer-to-member that indicates the type of the entity fetched. */ check_assertion(op2 != NULL); if (!is_ptr_to_member_type(op2->type)) break; does_fetch = TRUE; fetched_type = make_pm_selection_type(operand_type, op2->type); break; case eok_dot_pm_func_ptr: case eok_points_to_pm_func_ptr: /* These fetch a function pointer, from a pointer to member or a virtual function table. */ does_fetch = TRUE; fetched_type = node->type; break; case eok_subscript: /* Swap operands if the pointer operand is second. */ if (op2 != NULL && is_pointer_type(op2->type)) { an_expr_node_ptr saved_op1 = op1; op1 = op2; op2 = saved_op1; } /* if */ FALLTHROUGH case eok_indirect: case eok_ref_indirect: /* An indirection operation (e.g., "*") fetches the thing pointed to. Subscripting is similar. */ /* Watch out for errors and prototype instantiations. */ if (!is_pointer_type(op1->type)) break; does_fetch = TRUE; fetched_type = type_pointed_to(op1->type); break; #if GNU_VECTOR_TYPES_ALLOWED case eok_vector_subscript: if (!is_vector_type(op1->type)) break; does_fetch = TRUE; fetched_type = skip_typerefs(op1->type) ->variant.vector.element_type; break; #endif /* GNU_VECTOR_TYPES_ALLOWED */ #if MICROSOFT_EXTENSIONS_ALLOWED case eok_cli_subscript: /* C++/CLI array subscript operator. */ if (!is_handle_type(op1->type)) break; { a_type_ptr arr_type = type_pointed_to(op1->type); if (is_cli_array_type(arr_type)) { does_fetch = TRUE; fetched_type = cli_array_element_type(arr_type); } /* if */ } break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case eok_ref_cast: case eok_lvalue_adjust: case eok_ref_dynamic_cast: /* Type adjustment of an lvalue, or cast to a reference type, with an implicit lvalue-to-rvalue conversion afterwards. */ does_fetch = TRUE; fetched_type = node->type; break; case eok_unbox: case eok_unbox_lvalue: /* A C++/CLI unbox operation with is_lvalue FALSE fetches the rvalue out of the box. */ does_fetch = TRUE; fetched_type = node->type; break; case eok_va_arg: does_fetch = TRUE; fetched_type = node->type; break; #if C99_IL_EXTENSIONS_SUPPORTED case eok_real_part: case eok_imag_part: /* These fetch something if the operand is a glvalue. */ if (!is_glvalue_node(op1)) break; does_fetch = TRUE; fetched_type = type_plus_qualifiers_from_second_type(node->type, op1->type); break; #endif /* C99_IL_EXTENSIONS_SUPPORTED */ case eok_lvalue_cast: /* Not rvalueable; when converted to an rvalue it gets rewritten as a normal cast. */ case eok_dot_static: /* Handled when the second operand is handled. */ case eok_points_to_static: /* Ditto. */ case eok_base_class_cast: /* Not rvalueable. */ case eok_derived_class_cast: /* Not rvalueable. */ default: break; } /* switch */ } break; case enk_param_ref: /* An rvalue parameter reference is conceptually a "fetch", but in the common case where such nodes appear in arguments of decltype, sizeof, etc., no actual fetching is involved. */ does_fetch = TRUE; fetched_type = node->type; break; default: break; } /* switch */ } /* if */ if (does_fetch && p_fetched_type != NULL) *p_fetched_type = fetched_type; return does_fetch; } /* node_does_fetch */ static a_boolean operation_has_side_effects( an_expr_node_ptr node, an_expr_or_stmt_traversal_block_ptr tblock) /* Examine the operation node "node" for side effects; called by the expression traversal routines. Return TRUE if the expression has a side effect, not considering any side effects in its operands. Set tblock->suppress_warning to TRUE if a warning about the expression doing nothing should be suppressed. */ { a_boolean has_side_effects = FALSE; a_type_ptr operand_type; an_expr_operator_kind op = node->variant.operation.kind; an_expr_node_ptr op1 = node->variant.operation.operands; switch (op) { case eok_post_incr: case eok_post_decr: case eok_pre_incr: case eok_pre_decr: case eok_assign: case eok_bassign: case eok_add_assign: case eok_subtract_assign: case eok_multiply_assign: case eok_divide_assign: case eok_remainder_assign: case eok_shiftl_assign: case eok_shiftr_assign: case eok_and_assign: case eok_or_assign: case eok_xor_assign: case eok_padd_assign: case eok_psubtract_assign: case eok_call: case eok_dot_member_call: case eok_points_to_member_call: case eok_dot_pm_call: case eok_points_to_pm_call: case eok_va_start: case eok_va_arg: case eok_va_end: case eok_va_copy: /* These all cause side effects. */ has_side_effects = TRUE; break; case eok_dot_vacuous_destructor_call: case eok_points_to_vacuous_destructor_call: /* A vacuous destructor call like p->int::~int(); is an expression that intentionally does nothing, so suppress the warning. */ tblock->suppress_warning = TRUE; break; case eok_dynamic_cast: if (is_template_param_type(node->type)) { /* A dynamic_cast to a template parameter type might be a cast to a reference type, which can throw an exception. */ has_side_effects = TRUE; } /* if */ break; case eok_ref_dynamic_cast: { /* A dynamic_cast to a reference from an object with a polymorphic class type can throw an exception. */ a_type_ptr dest_type = skip_typerefs(node->type); operand_type = op1->type; if (could_be_dependent_class_type(operand_type)) { has_side_effects = TRUE; } else if (is_polymorphic_class_type(operand_type)) { /* Some cases may end up having static_cast semantics. */ if (could_be_dependent_class_type(operand_type) || !(same_type_with_added_qualifiers(operand_type, dest_type, /*ignore_qualifiers=*/TRUE, (a_boolean *)NULL) || (is_class_struct_union_type(dest_type) && is_class_struct_union_type(operand_type) && find_base_class_of(operand_type, dest_type) != NULL))) { has_side_effects = TRUE; } /* if */ } /* if */ } /* if */ break; #if MICROSOFT_EXTENSIONS_ALLOWED case eok_assume: /* __assume(expr) intentionally does nothing, so suppress the warning. */ tblock->suppress_warning = TRUE; break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case eok_noexcept: /* noexcept doesn't have a side effect and doesn't evaluate its operand. */ tblock->suppress_subtree_walk = TRUE; break; case eok_lvalue_cast: case eok_ref_cast: case eok_lvalue_adjust: goto check_cast_destination_type; case eok_cast: #if C99_IL_EXTENSIONS_SUPPORTED if ((c99_mode || cpp11_mode) && (is_floating_type(node->type) || is_floating_type(op1->type))) { /* Floating-point conversions can cause side effects in C99 and C++11. */ goto c99_float_operations; } /* if */ #endif /* C99_IL_EXTENSIONS_SUPPORTED */ check_cast_destination_type: if (vla_enabled && type_has_side_effects(node->type)) { has_side_effects = TRUE; } /* if */ break; case eok_box: case eok_handle_to_box: case eok_unbox: case eok_unbox_lvalue: /* Box allocates an object on the managed heap. */ /* Unbox is a lot like a ref cast, but it does a runtime check. */ has_side_effects = TRUE; break; #if C99_IL_EXTENSIONS_SUPPORTED case eok_negate: case eok_add: case eok_subtract: case eok_multiply: case eok_divide: case eok_eq: case eok_ne: case eok_gt: case eok_lt: case eok_ge: case eok_le: case eok_spaceship: case eok_vector_eq: case eok_vector_ne: case eok_vector_gt: case eok_vector_lt: case eok_vector_ge: case eok_vector_le: case eok_gnu_min: case eok_gnu_max: /* In C99 and C++11, the floating-point status flags can be tested, so a floating-point operation is considered to have side effects. */ if (!c99_mode && !cpp11_mode) { break; } else if (!node_operator_type_kind_is(node, tk_float) && !node_operator_type_kind_is(node, tk_complex) && !node_operator_type_kind_is(node, tk_imaginary)) { break; } /* if */ c99_float_operations: { a_boolean fp_operations_can_cause_side_effects = FALSE; /* Floating-point operations can cause side effects unless FENV_ACCESS is set to off (or, in clang mode, to default, which clang treats as being "off"). Outside of the front end proper, we don't know the current state of that flag so we assume side effects are possible. */ if (!in_front_end) { fp_operations_can_cause_side_effects = TRUE; #if DO_IL_LOWERING } else if (il_lowering_underway) { fp_operations_can_cause_side_effects = TRUE; #endif /* DO_IL_LOWERING */ } else { fp_operations_can_cause_side_effects = (curr_fenv_access_state != (a_stdc_pragma_value)stdc_pv_off && !(clang_mode && curr_fenv_access_state == (a_stdc_pragma_value)stdc_pv_default)); } /* if */ if (fp_operations_can_cause_side_effects) has_side_effects = TRUE; } break; #endif /* C99_IL_EXTENSIONS_SUPPORTED */ default: /* Other operations have no side effects at this level. The operands might still cause side effects. */ break; } /* switch */ #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && node->variant.operation.requires_runtime_cast_check) { /* A C++/CLI safe_cast that includes a runtime check can throw an exception. */ has_side_effects = TRUE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ return has_side_effects; } /* operation_has_side_effects */ static void examine_expr_for_side_effect( an_expr_node_ptr node, an_expr_or_stmt_traversal_block_ptr tblock) /* Examine node for side effects; called by the expression traversal routines. Set tblock->result to TRUE if the expression has a side effect. Set tblock->suppress_warning to TRUE if a warning about the expression doing nothing should be suppressed. */ { a_boolean has_side_effects = FALSE; switch (node->kind) { case enk_error: /* Who knows what an error node might have done -- it might have had side effects. */ has_side_effects = TRUE; break; case enk_constant: if (is_error_constant(node_constant(node))) { /* An error constant might have been anything -- it might have had side effects. */ has_side_effects = TRUE; } else if (node_constant(node)->is_result_of_constexpr_call) { /* No warning for a constexpr function or constructor call result that is a constant. */ tblock->suppress_warning = TRUE; } /* if */ break; case enk_operation: has_side_effects = operation_has_side_effects(node, tblock); break; case enk_variable: #if USE_LAZY_INITIALIZATION_FOR_THREAD_LOCAL_VARIABLES if (node_variable(node)->is_thread_local) { /* When lazy initialization is used for thread_local variables, the node may have a side-effect. */ has_side_effects = TRUE; } /* if */ #endif /* USE_LAZY_INITIALIZATION_FOR_THREAD_LOCAL_VARIABLES */ /* See below for volatile fetch. */ break; case enk_temp_init: /* There is always at least the side effect of initializing the temporary. */ if (tblock->for_unused_variable_warning) { /* When checking the initializer of an unused variable for side effects to control a diagnostic, don't count the creation of the temporary itself. If the dynamic init has side effects (like calling a constructor), that will be counted when the dynamic init is handled. */ } else { has_side_effects = TRUE; } /* if */ break; case enk_condition: /* At the very least, this has the side effect of initializing something. */ has_side_effects = TRUE; break; case enk_new_delete: /* A new or delete always has a side effect. */ has_side_effects = TRUE; break; case enk_lambda: /* A lambda always has a side effect. */ has_side_effects = TRUE; break; #if MICROSOFT_EXTENSIONS_ALLOWED case enk_gcnew: /* A gcnew always has a side effect. */ has_side_effects = TRUE; break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case enk_throw: /* A throw always has a side effect. */ has_side_effects = TRUE; break; case enk_sizeof: case enk_datasizeof: /* A sizeof doesn't have side effects, but more than that its expression is unevaluated and can't have side effects. */ if (vla_enabled && node->variant.sizeof_info.is_type && is_vla_type(node->variant.sizeof_info.variant.type)) { /* However, sizeof a VLA type evaluates the bound expression. Only sizeof(type) can have side effects; for sizeof(expr), the VLA type was generated elsewhere. */ /* Assume side effects from the type. */ tblock->result = TRUE; tblock->terminate = TRUE; } else { tblock->suppress_subtree_walk = TRUE; } /* if */ break; case enk_sizeof_pack: /* sizeof...(T) has no side effects. */ has_side_effects = FALSE; tblock->suppress_subtree_walk = TRUE; break; case enk_alignof: /* An alignof doesn't have side effects, but more than that its expression is unevaluated and can't have side effects. */ has_side_effects = FALSE; tblock->suppress_subtree_walk = TRUE; break; case enk_typeid: { an_expr_node_ptr opnds = node->variant.typeid_info.type_with_opt_expr; if (opnds->next != NULL) { /* A typeid applied to an expression that is a pointer to a polymorphic class type can throw an exception if the pointer is NULL. */ if (node->variant.typeid_info.is_dynamic || could_be_dependent_class_type( opnds->variant.type_operand.type)) { has_side_effects = TRUE; } /* if */ } /* if */ } break; case enk_statement: /* Assume a statement expression has side effects. */ has_side_effects = TRUE; break; #if DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING /* Nodes generated by IL lowering for partial lowering of exception handling features. */ case enk_lowered_eh_construct: switch (node->variant.lowered_eh.kind) { case leck_caught_object_address: case leck_thrown_object_address: /* No side effects. */ break; default: has_side_effects = TRUE; break; } /* switch */ break; #endif /* DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING */ case enk_reuse_value: break; #if DO_IL_LOWERING && ABI_CHANGES_FOR_COVARIANT_VIRTUAL_FUNC_RETURN case enk_result_of_overriding_function: /* Node generated as part of the body of an entry function used as a wrapper for a call of an overriding virtual function with a covariant return type. */ /* Probably not expected, but give the safe answer just in case. */ has_side_effects = TRUE; break; #endif /* DO_IL_LOWERING && ABI_CHANGES_FOR_COVARIANT_VIRTUAL_FUNC_RETURN */ case enk_await: case enk_yield: has_side_effects = TRUE; break; case enk_fold: /* A generic representation of a fold-expression. The generic nature of this operation means that we do not know if it will produce a side-effect after substitution. Assume it will. */ has_side_effects = TRUE; break; case enk_pack_index: /* pack...[N] has no side effects. */ has_side_effects = FALSE; tblock->suppress_subtree_walk = TRUE; break; case enk_braced_init_list: case enk_type_operand: case enk_param_ref: case enk_requires: case enk_compound_req: case enk_nested_req: default: /* Others cause no side effects at this level. The subtree might still cause side effects. */ break; } /* switch */ if (!has_side_effects) { a_type_ptr fetched_type; if (node_does_fetch(node, &fetched_type) && is_volatile_qualified_type(fetched_type)) { /* An operation that loads a volatile object has a side effect. */ has_side_effects = TRUE; } else if (could_be_dependent_class_type(node->type) && is_operation_node(node)) { /* A node with a template parameter type is considered to have side effects. This is because it's possible that when the type is actually known an overloaded operator function would be chosen, which would mean a function call. */ has_side_effects = TRUE; if (is_glvalue_node(node)) { a_constant_ptr local_con = local_constant(); if (constant_glvalue_address(node, local_con, /*address_escapes=*/FALSE)) { /* Don't consider an expression whose address is constant to have side effects. This is a detail, but helps ensure that we get the same declared-but-not-referenced warnings regardless of how aggressively we try to fold addressing expressions to constants. */ has_side_effects = FALSE; } /* if */ release_local_constant(&local_con); } /* if */ } /* if */ } /* if */ if (has_side_effects) { /* The node has a side effect, even without considering its operands. */ tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* examine_expr_for_side_effect */ static void set_up_side_effect_traversal_block( an_expr_or_stmt_traversal_block_ptr tblock) /* Set up the control block used for the side effect discovery traversal. */ { clear_expr_or_stmt_traversal_block(tblock); tblock->process_expr = examine_expr_for_side_effect; tblock->process_dynamic_init = examine_dynamic_init_for_side_effect; tblock->process_constant = examine_constant_for_side_effect; tblock->process_template_parameter_constants_and_expressions = TRUE; } /* set_up_side_effect_traversal_block */ a_boolean node_has_side_effects(an_expr_node_ptr node, a_boolean *suppress_warning) /* Return TRUE if the indicated expression node has side effects. If suppress_warning != NULL, return *suppress_warning TRUE if a warning about the expression doing nothing should be suppressed. */ { an_expr_or_stmt_traversal_block tblock; set_up_side_effect_traversal_block(&tblock); traverse_expr(node, &tblock); if (suppress_warning != NULL) *suppress_warning = tblock.suppress_warning; return tblock.result; } /* node_has_side_effects */ a_boolean expr_list_has_side_effects(an_expr_node_ptr expr_list, a_boolean *suppress_warning) /* Return TRUE if the indicated expression list has side effects. If suppress_warning != NULL, return *suppress_warning TRUE if a warning about the expression list doing nothing should be suppressed. */ { an_expr_or_stmt_traversal_block tblock; set_up_side_effect_traversal_block(&tblock); traverse_expr_list(expr_list, &tblock); if (suppress_warning != NULL) *suppress_warning = tblock.suppress_warning; return tblock.result; } /* expr_list_has_side_effects */ a_boolean dynamic_init_has_side_effects(a_dynamic_init_ptr dip, a_boolean for_unused_var, a_boolean *suppress_warning) /* Return TRUE if the indicated dynamic initialization has side effects, i.e., it does something other than just return a value for the initialization. If for_unused_var is TRUE, this test is for the initializer on an unused variable, so certain things that nominally have a side effect but don't really do anything productive are not counted. If suppress_warning != NULL, return *suppress_warning TRUE if a warning about the dynamic initialization doing nothing should be suppressed. */ { an_expr_or_stmt_traversal_block tblock; set_up_side_effect_traversal_block(&tblock); tblock.for_unused_variable_warning = for_unused_var; traverse_dynamic_init(dip, &tblock); if (suppress_warning != NULL) *suppress_warning = tblock.suppress_warning; return tblock.result; } /* dynamic_init_has_side_effects */ a_boolean is_invariant_expr(an_expr_node_ptr expr, a_boolean vars_can_change, a_boolean treat_as_potential_prvalue) /* Return TRUE if the indicated expression is invariant, meaning it has no side effects and will give the same value if evaluated more than once. The expression can be a prvalue or a glvalue; for a glvalue, invariant means its address will not change (and it has no side effects). vars_can_change indicates whether the values of variables should be considered to be changeable between successive evaluations for purposes of this determination. treat_as_potential_prvalue is TRUE in cases where the eventual value category of expr is not yet known (and can make a difference in determining whether or not the expression is invariant). treat_as_potential_prvalue should always be FALSE when called during lowering (as lvalueness is known at that time). */ { a_boolean is_invariant = FALSE; expr = skip_parens(expr); if (vars_can_change) { /* Variable values can change. */ if (is_constant_node(expr) || is_routine_node(expr)) { /* Constants and the address of a function are invariant. */ is_invariant = TRUE; } else if (is_glvalue_node(expr) && !treat_as_potential_prvalue) { /* Glvalue cases. */ if (is_variable_node(expr) || expr->kind == (an_expr_node_kind)enk_param_ref) { /* An lvalue for a variable is invariant. Note that local variables are considered invariant even though their addresses may change between invocations of the function. enk_param_ref nodes represent references to parameters within the function header, and as such are essentially variable references. An enk_param_ref node for "this" isn't an lvalue and therefore wouldn't get here. */ is_invariant = TRUE; } else if (is_operation_node(expr)) { an_expr_node_ptr op1 = expr->variant.operation.operands; an_expr_node_ptr op2 = op1->next; if (node_operator_is(expr, eok_dot_field) || node_operator_is(expr, eok_points_to_field) || node_operator_is(expr, eok_indirect)) { /* A glvalue a.b is invariant if a is invariant. */ /* Likewise for a->b and *a. */ is_invariant = is_invariant_expr(op1, vars_can_change, /*treat_as_potential_prvalue=*/FALSE); } else if (node_operator_is(expr, eok_subscript) || node_operator_is(expr, eok_vector_subscript)) { /* An lvalue a[b] is invariant if a and b are invariant. */ is_invariant = is_invariant_expr(op1, vars_can_change, /*treat_as_potential_prvalue=*/FALSE) && is_invariant_expr(op2, vars_can_change, /*treat_as_potential_prvalue=*/FALSE); } /* if */ } /* if */ } else { /* Non-lvalue cases. */ if (is_variable_node(expr)) { if (node_variable(expr)->source_corresp.name == NULL) { /* An unnamed variable is a temporary. Assume that such a thing is not changed in the "vars_can_change" mode. This is important, because if the expression has been assigned to a temporary once, we want to use that temporary directly on subsequent calls to make_reusable_copy. */ is_invariant = TRUE; } /* if */ } else if (is_operation_node(expr)) { an_expr_node_ptr op1 = expr->variant.operation.operands; if (node_operator_is(expr, eok_address_of) || node_operator_is(expr, eok_array_to_pointer)) { /* "&" applied to an invariant lvalue is invariant. */ /* Likewise for array decay. Watch out for array rvalues. */ if (op1->is_lvalue) { is_invariant = is_invariant_expr(op1, vars_can_change, /*treat_as_potential_prvalue=*/FALSE); } /* if */ } /* if */ } /* if */ } /* if */ } else { /* Variables cannot change. See if the expression has side effects. */ if (!node_has_side_effects(expr, (a_boolean *)NULL)) is_invariant = TRUE; } /* if */ return is_invariant; } /* is_invariant_expr */ static void set_up_matching_node_walk( an_expr_or_stmt_traversal_block_ptr tblock, a_traversal_expr_process_function_ptr check_node, a_boolean follow_folded_exprs) /* Prepare tblock for a walk that offers each node of an expression tree to check_node. The walk covers constants and the constants and expressions inside template parameter constants. If follow_folded_exprs is TRUE, it also follows the expressions recorded for folded constants. */ { clear_expr_or_stmt_traversal_block(tblock); tblock->process_expr = check_node; tblock->process_non_dynamic_constants = TRUE; tblock->process_expressions_for_constants = follow_folded_exprs; tblock->process_template_parameter_constants_and_expressions = TRUE; } /* set_up_matching_node_walk */ /* The number of expressions recorded for constants that a walk performed by expr_tree_has_matching_node may follow before it is abandoned in favor of one that records the expressions it has already followed. Walks that follow fewer than this are the overwhelming majority, and for most of those the record would cost more than the repeated visits it saves. */ #define MAX_SHARED_EXPRS_WITHOUT_RECORDING 1000 static a_boolean expr_tree_has_matching_node( an_expr_node_ptr expr, a_traversal_expr_process_function_ptr check_node, a_boolean follow_folded_exprs) /* Return TRUE if check_node reports a match for one of the nodes of the expression tree rooted in expr, which may be NULL. check_node is called for each node in turn and reports a match by setting the result and terminate flags of the traversal block passed to it; its verdict must not depend on how often it is offered a given node, as a node may be reached more than once. follow_folded_exprs asks the walk to follow the expressions recorded for folded constants. Following those recorded expressions means walking a graph, because they are shared, and the repeated visits that come of covering such a graph as though it were a tree grow exponentially with its depth. A walk that finds no match while following MAX_SHARED_EXPRS_WITHOUT_RECORDING of them is therefore abandoned and performed again, this time recording them so that none is followed twice. A walk that does not follow them has no such graph and does not use that bound. */ { an_expr_or_stmt_traversal_block tblock; a_boolean result = FALSE; if (expr != NULL) { set_up_matching_node_walk(&tblock, check_node, follow_folded_exprs); if (follow_folded_exprs) { tblock.shared_expr_budget = MAX_SHARED_EXPRS_WITHOUT_RECORDING; } /* if */ traverse_expr(expr, &tblock); if (follow_folded_exprs && !tblock.result && tblock.shared_expr_budget == 0) { Ptr_set visited_shared_exprs(/*mask_width=*/6u); set_up_matching_node_walk(&tblock, check_node, follow_folded_exprs); tblock.visited_shared_exprs = &visited_shared_exprs; traverse_expr(expr, &tblock); } /* if */ result = tblock.result; } /* if */ return result; } /* expr_tree_has_matching_node */ static void check_for_reference_to_local_entity( an_expr_node_ptr expr, an_expr_or_stmt_traversal_block_ptr tblock) /* Called via traverse_expr from expr_has_reference_to_local_entity; sets tblock->result to TRUE and terminates the traversal if expr is an enk_variable node that refers to a variable in a local scope or if expr is an enk_statement node allocated in function-scope memory. */ { if (expr != NULL && ((is_variable_node(expr) && node_variable(expr)->source_corresp.enclosing_routine != NULL) || (expr->kind == enk_statement && !in_file_scope(expr->variant.statement)))) { tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* check_for_reference_to_local_entity */ a_boolean expr_has_reference_to_local_entity(an_expr_node_ptr expr) /* Return TRUE if any of the nodes in the expression tree rooted in expr (which may be NULL) is an enk_variable node that refers to a variable in a local scope or an enk_statement node allocated in function-scope memory. */ { return expr_tree_has_matching_node(expr, check_for_reference_to_local_entity, /*follow_folded_exprs=*/TRUE); } /* expr_has_reference_to_local_entity */ static void check_for_reused_value_init( an_expr_node_ptr expr, an_expr_or_stmt_traversal_block_ptr tblock) /* Called via traverse_expr from expr_has_reused_value_init; sets tblock->result to TRUE and terminates the traversal if expr is an enk_temp_init node whose dynamic initialization computes a value that is reused elsewhere. */ { if (expr != NULL && node_is(expr, enk_temp_init) && expr->variant.init.dynamic_init->is_reused_value) { tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* check_for_reused_value_init */ a_boolean expr_has_reused_value_init(an_expr_node_ptr expr) /* Return TRUE if the expression tree rooted in expr, which may be NULL, contains the initialization of a temporary whose value is reused by an enk_reuse_value node elsewhere in the enclosing expression (see make_expr_reusable_copy). Folding such an expression to a constant would take that initialization out of the code that is generated for the enclosing expression, leaving the reuses with no value to use, so the folding must be suppressed. */ { return expr_tree_has_matching_node(expr, check_for_reused_value_init, /*follow_folded_exprs=*/FALSE); } /* expr_has_reused_value_init */ /* Variables used for detecting mixed memory regions in an expression tree. They are set to FALSE by mixed_regions_in_expr_tree and updated by check_node_for_mixed_memory_regions during the tree traversal. */ STATIC_THREAD a_boolean tree_has_file_scope_node; STATIC_THREAD a_boolean tree_has_local_node; static void check_node_for_mixed_memory_regions( an_expr_node_ptr expr, an_expr_or_stmt_traversal_block_ptr tblock) /* Called via traverse_expr from mixed_regions_in_expr_tree; sets tblock->result to TRUE and terminates the traversal if expr is allocated in file-scope memory and a local node or reference to a local variable has been seen or vice versa. */ { if (expr != NULL) { if (in_file_scope(expr)) { tree_has_file_scope_node = TRUE; if (is_variable_node(expr) && node_variable(expr)->source_corresp.enclosing_routine != NULL) { tree_has_local_node = TRUE; } /* if */ } else { tree_has_local_node = TRUE; } /* if */ if (tree_has_file_scope_node && tree_has_local_node) { tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* if */ } /* check_node_for_mixed_memory_regions */ a_boolean mixed_regions_in_expr_tree(an_expr_node_ptr expr) /* Return TRUE if the tree rooted in expr contains both nodes allocated in the file-scope memory region and nodes that are allocated in a local memory region or designate a block-scope variable; otherwise, return FALSE. */ { tree_has_file_scope_node = FALSE; tree_has_local_node = FALSE; return expr_tree_has_matching_node(expr, check_node_for_mixed_memory_regions, /*follow_folded_exprs=*/TRUE); } /* mixed_regions_in_expr_tree */ STATIC_THREAD a_variable_ptr last_routine_scope_variable_found; /* The last variable found by check_for_routine_scope_variable. */ static void check_for_routine_scope_variable( an_expr_node_ptr expr, an_expr_or_stmt_traversal_block_ptr tblock) /* Called via traverse_expr from expr_has_reference_to_routine_scope_variable; sets tblock->result to TRUE and terminates the traversal if expr refers to a variable in a local scope, either directly as an enk_variable node or through the capture list of a lambda. */ { if (expr != NULL) { a_variable_ptr var = NULL; if (is_variable_node(expr)) { var = node_variable(expr); } else if (node_is(expr, enk_lambda)) { /* The captures of a lambda name variables of the enclosing function, but they are recorded in the lambda entry rather than in the expression tree, so the traversal does not reach them. */ for (a_lambda_capture_ptr lcp = expr->variant.init.source.lambda->capture_list; lcp != NULL && var == NULL; lcp = lcp->next) { /* Only a plain capture names a variable; for the other forms the "captured" union holds a field or an initializer instead. */ if (!lcp->is_init_capture && !lcp->is_indirect_init_capture) { var = lcp->captured.variable; } /* if */ } /* for */ } /* if */ if (var != NULL && !in_file_scope(var)) { last_routine_scope_variable_found = var; tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* if */ } /* check_for_routine_scope_variable */ a_boolean expr_has_reference_to_routine_scope_variable(an_expr_node_ptr expr) /* Return TRUE if the expression tree rooted in expr (which may be NULL) refers to a variable in a local scope, either through an enk_variable node or through the captures of a lambda. Original expressions recorded for nested folded constants are examined, too: A copy of this tree for a local-expr-node-ref includes those expressions, so the local-expr-node-ref must be recorded in the function that owns the variables they name. */ { return expr_tree_has_matching_node(expr, check_for_routine_scope_variable, /*follow_folded_exprs=*/TRUE); } /* expr_has_reference_to_routine_scope_variable */ a_variable_ptr get_routine_scope_variable_found(void) /* Return the local variable found by the last call of expr_has_reference_to_routine_scope_variable that produced TRUE. */ { return last_routine_scope_variable_found; } /* get_routine_scope_variable_found */ static a_routine_ptr alloc_or_dealloc_routine_from_new_delete( an_expr_node_ptr expr) /* Return the allocation or deallocation routine that will be called for the indicated new or delete, or NULL if the routine cannot be determined. */ { a_routine_ptr rout = NULL; a_new_delete_supplement_ptr ndsp; check_assertion(expr->kind == enk_new_delete); ndsp = expr->variant.new_delete; rout = ndsp->routine; if (rout == NULL) { /* An implied allocation or deallocation routine. Figure out which routine gets called. */ a_type_ptr base_type=new_delete_base_type_from_operation_type(ndsp->type); if (ndsp->is_new) { /* Allocation routine. */ if (is_array_type(ndsp->type) && new_or_delete_type_requires_array_handling(base_type, /*check_constructor=*/TRUE)) { /* Array new. */ #if NEW_AND_DELETE_FOR_ARRAY_CAN_BE_FOLDED_INTO_RUNTIME_ROUTINE an_opname_kind array_opname_kind = array_new_and_delete_enabled ? (an_opname_kind)onk_array_new : (an_opname_kind)onk_new; a_boolean ambiguous; a_symbol_ptr sym = opname_function_symbol(array_opname_kind); check_assertion(sym != NULL); sym = find_default_operator_new_sym(sym, &ambiguous); check_assertion(sym != NULL && is_simple_function_symbol(sym)); rout = sym->variant.routine.ptr; #else /* !NEW_AND_DELETE_FOR_ARRAY_CAN_BE_FOLDED_INTO_RUNTIME_ROUTINE */ unexpected_condition(); #endif /* NEW_AND_DELETE_FOR_ARRAY_CAN_BE_FOLDED_INTO_RUNTIME_ROUTINE */ } else { /* Non-array new. */ #if NEW_CAN_BE_FOLDED_INTO_CTOR if (is_class_struct_union_type(base_type)) { rout = class_type_supp(skip_typerefs(base_type))-> assoc_operator_new_routine; } /* if */ #endif /* NEW_CAN_BE_FOLDED_INTO_CTOR */ } /* if */ } else { /* Deallocation routine. */ a_boolean check_constructor = TRUE; #if IA64_ABI /* The IA-64 ABI requires no cookie for a class array new where the class has a constructor but no destructor. */ check_constructor = FALSE; #endif /* IA64_ABI */ if (ndsp->array_delete && new_or_delete_type_requires_array_handling(base_type, check_constructor)) { /* Array delete. */ #if NEW_AND_DELETE_FOR_ARRAY_CAN_BE_FOLDED_INTO_RUNTIME_ROUTINE an_opname_kind array_opname_kind = array_new_and_delete_enabled ? (an_opname_kind)onk_array_delete : (an_opname_kind)onk_delete; a_boolean ambiguous; a_symbol_ptr sym = opname_function_symbol(array_opname_kind); check_assertion(sym != NULL); sym = find_default_operator_delete_sym(sym, base_type, &ambiguous); check_assertion(sym != NULL && is_simple_function_symbol(sym)); rout = sym->variant.routine.ptr; #else /* !NEW_AND_DELETE_FOR_ARRAY_CAN_BE_FOLDED_INTO_RUNTIME_ROUTINE */ unexpected_condition(); #endif /* NEW_AND_DELETE_FOR_ARRAY_CAN_BE_FOLDED_INTO_RUNTIME_ROUTINE */ } else { /* Non-array delete. */ #if DELETE_CAN_BE_FOLDED_INTO_DTOR if (is_class_struct_union_type(base_type)) { rout = class_type_supp(skip_typerefs(base_type))-> assoc_operator_delete_routine; } /* if */ #endif /* DELETE_CAN_BE_FOLDED_INTO_DTOR */ } /* if */ } /* if */ } /* if */ return rout; } /* alloc_or_dealloc_routine_from_new_delete */ static void examine_constant_for_throwing_exception( a_constant_ptr con, an_expr_or_stmt_traversal_block_ptr tblock) /* Examine the indicated constant to see if it might throw an exception; called by the expression traversal routines. Set tblock->result to TRUE if so. */ { if (con->kind == (a_constant_repr_kind)ck_error) { /* An error constant could have been anything, including something that throws. */ tblock->result = TRUE; tblock->terminate = TRUE; } else if (con->kind == (a_constant_repr_kind)ck_template_param) { /* Assume that a template-dependent constant might do anything. */ tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* examine_constant_for_throwing_exception */ static void examine_dynamic_init_for_throwing_exception( a_dynamic_init_ptr dip, an_expr_or_stmt_traversal_block_ptr tblock) /* Examine the indicated dynamic initialization to see if it might throw an exception; called by the expression traversal routines. Set tblock->result to TRUE if so. */ { a_boolean might_throw = FALSE; if (dip->destructor != NULL && !is_non_throwing_routine(dip->destructor)) { /* The destructor call might throw. */ might_throw = TRUE; } else { switch (dip->kind) { case dik_constructor: /* A constructor call might throw. */ { a_routine_ptr rout = dip->variant.constructor.ptr; /* A null routine means a dependent call, so assume the selected constructor might throw. */ if (rout == NULL || !is_non_throwing_routine(rout)) might_throw = TRUE; } break; case dik_constant: /* Within the operand of the noexcept operator, a constructor call that was folded to a constant is still potentially throwing since C++17 (P0003R5). Recover and examine the original (backing) call so that the fold does not hide it from the noexcept determination. This is reached for the class-type member initializers deliberately left in dik_constant form under a ck_dynamic_init for this purpose (see aggr_init_constant_from_field_initializer). */ if (expr_stack != NULL && expr_stack->in_noexcept_operand_expression && !core_constant_expr_is_noexcept) { an_expr_node_ptr backing = expr_node_from_constant(dip->variant.constant.ptr); if (backing != NULL && expr_might_throw(backing)) { might_throw = TRUE; } /* if */ } /* if */ break; default: /* Others do not throw at this level. The subtree might still throw. */ break; } /* switch */ } /* if */ if (might_throw) { /* The dynamic initialization might throw, even without considering its subtree. */ tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* examine_dynamic_init_for_throwing_exception */ static void examine_expr_for_throwing_exception( an_expr_node_ptr node, an_expr_or_stmt_traversal_block_ptr tblock) /* Examine node to see if it might throw an exception; called by the expression-traversal routines. Set tblock->result to TRUE if so. */ { a_boolean might_throw = FALSE; switch (node->kind) { case enk_error: /* Who knows what an error node might have done -- it might have thrown something. */ might_throw = TRUE; break; case enk_operation: if (is_call_node(node)) { /* For a call, see if the called routine might throw. */ an_expr_node_ptr op1 = node->variant.operation.operands; a_routine_ptr rout = routine_from_function_expr(op1); if (rout != NULL) { if (!is_non_throwing_routine(rout)) { might_throw = TRUE; if (rout->is_declared_constexpr && gpp_version_is(<90000)) { /* GCC versions prior to 9.x appear to check calls for being core constant expressions (but not non-call components of an expression; e.g., "true ? 0 : throw 0" is considered potentially throwing). */ a_diag_list diag_list; clear_diag_list(&diag_list); if (is_core_constant_expr(node, &diag_list)) { might_throw = FALSE; } /* if */ discard_more_info_list(&diag_list); } /* if */ } /* if */ } else { a_type_ptr ptr_type = op1->type; a_type_ptr func_type = NULL; might_throw = TRUE; if (is_pointer_type(ptr_type)) { func_type = f_skip_typerefs(type_pointed_to(ptr_type)); } else if (is_ptr_to_member_type(ptr_type)) { func_type = f_skip_typerefs(pm_member_type(ptr_type)); } /* if */ if (func_type != NULL) { if (is_function_type(func_type) && is_nothrow_type(func_type)) { might_throw = FALSE; } /* if */ } /* if */ } /* if */ } else if (node_operator_is(node, eok_ref_dynamic_cast)) { /* A dynamic_cast to a reference type can throw. */ might_throw = TRUE; } /* if */ break; case enk_new_delete: /* A new or delete throws if the allocation or deallocation routine throws. The freeing_of_storage_on_exception dynamic init on a new, if present, will be processed by the normal tree scan. */ { a_routine_ptr rout = alloc_or_dealloc_routine_from_new_delete(node); if (rout != NULL && !is_non_throwing_routine(rout)) might_throw = TRUE; } break; case enk_throw: might_throw = TRUE; break; case enk_sizeof: case enk_datasizeof: case enk_alignof: case enk_sizeof_pack: case enk_pack_index: tblock->suppress_subtree_walk = TRUE; break; case enk_typeid: { an_expr_node_ptr opnds = node->variant.typeid_info.type_with_opt_expr; if (opnds->next != NULL) { /* A typeid applied to an expression that is a pointer to a polymorphic class type can throw an exception if the pointer is NULL. */ if (node->variant.typeid_info.is_dynamic || could_be_dependent_class_type( opnds->variant.type_operand.type)) { might_throw = TRUE; } /* if */ } /* if */ } break; case enk_statement: /* Assume a statement expression might throw. We don't currently have a way to walk the statements. */ might_throw = TRUE; break; #if DO_IL_LOWERING && ABI_CHANGES_FOR_COVARIANT_VIRTUAL_FUNC_RETURN case enk_result_of_overriding_function: /* Node generated as part of the body of an entry function used as a wrapper for a call of an overriding virtual function with a covariant return type. */ /* Probably not expected, but give the safe answer just in case. */ might_throw = TRUE; break; #endif /* DO_IL_LOWERING && ABI_CHANGES_FOR_COVARIANT_VIRTUAL_FUNC_RETURN */ case enk_constant: case enk_variable: case enk_temp_init: case enk_lambda: case enk_condition: case enk_param_ref: case enk_braced_init_list: #if DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING /* Nodes generated by IL lowering for partial lowering of exception handling features. */ case enk_lowered_eh_construct: #endif /* DO_IL_LOWERING && !DO_FULL_PORTABLE_EH_LOWERING */ case enk_reuse_value: #if MICROSOFT_EXTENSIONS_ALLOWED case enk_gcnew: #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ default: /* Others do not throw at this level. The subtree might still throw. */ break; } /* switch */ if (might_throw) { /* The node might throw, even without considering its operands. */ tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* examine_expr_for_throwing_exception */ static void set_up_might_throw_traversal_block( an_expr_or_stmt_traversal_block_ptr tblock) /* Set up the control block used for the might-throw discovery traversal. */ { clear_expr_or_stmt_traversal_block(tblock); tblock->process_expr = examine_expr_for_throwing_exception; tblock->process_dynamic_init = examine_dynamic_init_for_throwing_exception; tblock->process_constant = examine_constant_for_throwing_exception; } /* set_up_might_throw_traversal_block */ a_boolean expr_might_throw(an_expr_node_ptr expr) /* Return TRUE if evaluating the given expression might cause an exception to be thrown. See the definition of the "noexcept" operator in the C++11 standard [expr.unary.noexcept]. */ { a_boolean result = TRUE; a_diag_list diag_list; clear_diag_list(&diag_list); if (constexpr_enabled && core_constant_expr_is_noexcept && is_core_constant_expr(expr, &diag_list)) { /* In C++11 and C++14, core constant-expressions were always "noexcept". However, it appears no other compilers ever implemented that rule, and C++17 dropped the rule (through paper P0003R5). We leave the pre-C++17 behavior available under a separate flag. */ result = FALSE; } else { an_expr_or_stmt_traversal_block tblock; set_up_might_throw_traversal_block(&tblock); if (exceptions_enabled) { traverse_expr(expr, &tblock); } /* if */ result = tblock.result; } /* if */ discard_more_info_list(&diag_list); return result; } /* expr_might_throw */ a_boolean dynamic_init_might_throw(a_dynamic_init_ptr dip) /* Return TRUE if evaluating the given initialization might cause an exception to be thrown. */ { a_boolean result = FALSE; an_expr_node_ptr backing = NULL; if (exceptions_enabled && dyn_init_is(dip, dik_constant)) { backing = expr_node_from_constant(dip->variant.constant.ptr); } /* if */ if (!exceptions_enabled) { /* Nothing can be thrown. */ } else if (backing != NULL) { /* The initializer is a single call (e.g., to a constructor) that was folded to a constant. Although folded, running the enclosing default constructor still notionally performs that call, which since C++17 (P0003R5) is potentially throwing unless it has a non-throwing exception specification. Examine the original (backing) expression rather than the constant, which by itself carries no such information. Aggregate initializers (e.g., for arrays) do not have such a top-level backing expression and are handled by the normal traversal below. */ result = expr_might_throw(backing); if (!result && dip->destructor != NULL && !is_non_throwing_routine(dip->destructor)) { result = TRUE; } /* if */ } else { an_expr_or_stmt_traversal_block tblock; set_up_might_throw_traversal_block(&tblock); traverse_dynamic_init(dip, &tblock); result = tblock.result; } /* if */ return result; } /* dynamic_init_might_throw */ static void examine_dynamic_init_for_nontrivial_call( a_dynamic_init_ptr dip, an_expr_or_stmt_traversal_block_ptr tblock) /* This routine is called by the expression traversal routines. It sets tblock->result and tblock->terminate to TRUE if the given dynamic initialization involves a call to anything not known to be a trivial special member. */ { if (dip->destructor != NULL && !dip->destructor->is_trivial_destructor) { tblock->result = TRUE; tblock->terminate = TRUE; } else if (dip->kind == (a_dynamic_init_kind)dik_constructor) { a_routine_ptr ctor = dip->variant.constructor.ptr; if (ctor != NULL && !ctor->is_trivial_default_constructor && !ctor->is_trivial_copy_function) { /* Initialization is through a nontrivial constructor. */ tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* if */ } /* examine_dynamic_init_for_nontrivial_call */ static void examine_expr_for_nontrivial_call( an_expr_node_ptr node, an_expr_or_stmt_traversal_block_ptr tblock) /* This routine is called by the expression traversal routines. It sets tblock->result and tblock->terminate to TRUE if the given node is a call to anything not known to be a trivial special member. */ { if (is_call_node(node)) { an_expr_node_ptr op1 = node->variant.operation.operands; a_routine_ptr rp = routine_from_function_expr(op1); if (rp != NULL && (rp->is_trivial_default_constructor || rp->is_trivial_copy_function || rp->is_trivial_destructor)) { /* A call to a trivial special member. */ } else { /* Any other call is treated as nontrivial. */ tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* if */ } /* examine_expr_for_nontrivial_call */ a_boolean expr_calls_nontrivial_function(an_expr_node_ptr expr) /* Return TRUE if the evaluation of the given expression involves a call to any function other than a trivial default constructor, trivial copy constructor, trivial copy assignment operator, or trivial destructor. */ { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = examine_expr_for_nontrivial_call; tblock.process_dynamic_init = examine_dynamic_init_for_nontrivial_call; traverse_expr(expr, &tblock); return tblock.result; } /* expr_calls_nontrivial_function */ static void examine_dynamic_init_for_nontrivial_ctor( a_dynamic_init_ptr dip, an_expr_or_stmt_traversal_block_ptr tblock) /* This routine is called by the expression traversal routines. It sets tblock->result and tblock->terminate to TRUE if the given dynamic initialization involves a call to a constructor not known to be trivial. */ { if (dip->kind == (a_dynamic_init_kind)dik_constructor) { a_routine_ptr ctor = dip->variant.constructor.ptr; if (ctor != NULL && !ctor->is_trivial_default_constructor && !ctor->is_trivial_copy_function) { /* Initialization is through a nontrivial constructor. */ tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* if */ } /* examine_dynamic_init_for_nontrivial_ctor */ static void examine_expr_for_nontrivial_ctor( an_expr_node_ptr node, an_expr_or_stmt_traversal_block_ptr tblock) /* This routine is called by the expression traversal routines. It sets tblock->result and tblock->terminate to TRUE if the given node is a call to a constructor not known to be trivial. */ { if (is_call_node(node)) { an_expr_node_ptr op1 = node->variant.operation.operands; a_routine_ptr rp = routine_from_function_expr(op1); if (rp != NULL && special_kind_is(rp, sfk_constructor) && !(rp->is_trivial_default_constructor ||rp->is_trivial_copy_function)) { /* A call to a nontrivial constructor. */ tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* if */ } /* examine_expr_for_nontrivial_ctor */ a_boolean expr_calls_nontrivial_ctor(an_expr_node_ptr expr) /* Return TRUE if the evaluation of the given expression involves a call to a nontrivial constructor. */ { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = examine_expr_for_nontrivial_ctor; tblock.process_dynamic_init = examine_dynamic_init_for_nontrivial_ctor; traverse_expr(expr, &tblock); return tblock.result; } /* expr_calls_nontrivial_ctor */ static void check_expr_for_statement_expression( an_expr_node_ptr expr, an_expr_or_stmt_traversal_block_ptr tblock) /* Called from traverse_expr to check whether the expression is a statement expression. */ { if (expr->kind == enk_statement) { /* This expression is a statement expression. Stop the tree walk. */ tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* check_expr_for_statement_expression */ a_boolean has_statement_expression(ARG_UNUSED an_expr_node_ptr expr) /* Return whether expr contains a statement expression (a GNU extension). */ { a_boolean result = FALSE; an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = check_expr_for_statement_expression; traverse_expr(expr, &tblock); result = tblock.result; return result; } /* has_statement_expression */ #if !STANDALONE_UTILITY_PROGRAM static inline void find_scope_for_entity_pragmas( char *entity_ptr, an_il_entry_kind entity_kind, a_source_correspondence_ptr entity_scp, a_scope_ptr func_or_templ_scope, a_scope_ptr *il_scope, a_scope_pointers_block_ptr *pointers_block); static void remove_pragmas_associated_with_entity( char *ptr, an_il_entry_kind kind, a_source_correspondence_ptr scp) /* Remove pragmas associated with the given entity, which is being eliminated from the IL. If the entity has an associated source_correspondence, scp points to it. */ { if ((scp == NULL || scp->has_associated_pragma) && innermost_function_scope != NULL) { a_scope_ptr il_scope; a_scope_pointers_block_ptr pointers_block; a_pragma_ptr pp, prev_pp = NULL, next_pp; find_scope_for_entity_pragmas(ptr, kind, scp, innermost_function_scope, &il_scope, &pointers_block); for (pp = il_scope->pragmas; pp != NULL; pp = next_pp) { next_pp = pp->next; if (pp->entity.kind == kind && pp->entity.ptr == ptr) { /* Unlink the pragma and break its reference to the eliminated entity. The pragma entry itself may still be reachable through a source sequence entry that has not yet been removed. */ if (prev_pp == NULL) { il_scope->pragmas = next_pp; } else { prev_pp->next = next_pp; } /* if */ if (next_pp == NULL && pointers_block != NULL) { pointers_block->last_pragma = prev_pp; } /* if */ pp->next = NULL; pp->entity.kind = iek_none; pp->entity.ptr = NULL; } else { prev_pp = pp; } /* if */ } /* for */ if (scp != NULL) scp->has_associated_pragma = FALSE; } /* if */ } /* remove_pragmas_associated_with_entity */ static void remove_pragmas_associated_with_statement( a_statement_ptr stmt, ARG_UNUSED an_expr_or_stmt_traversal_block_ptr tblock) /* Called from traverse_statement to remove pragmas associated with the given statement, which is being eliminated from the IL. tblock is ignored. */ { if (stmt->has_associated_pragma) { remove_pragmas_associated_with_entity((char*)stmt, iek_statement, (a_source_correspondence_ptr)NULL); stmt->has_associated_pragma = FALSE; } /* if */ if (stmt->kind == stmk_decl) { an_il_entity_list_entry_ptr entry = stmt->variant.decl.entities; for (; entry != NULL; entry = entry->next) { a_source_correspondence_ptr scp = source_corresp_for_il_entry(entry->entity.ptr, entry->entity.kind); if (scp != NULL && scp->has_associated_pragma) { remove_pragmas_associated_with_entity(entry->entity.ptr, entry->entity.kind, scp); } /* if */ } /* for */ } /* if */ if (stmt->kind == stmk_label && stmt->variant.label.ptr != NULL && !is_at_least_one_error()) { /* Once an error has been issued, label and control-flow descriptors may still be needed for error recovery. Leave those structures intact in that case. */ a_label_ptr label = stmt->variant.label.ptr; a_scope_ptr scope = label->source_corresp.parent_scope; /* A discarded statement expression can contain label definitions. Labels are also reachable from the containing scope's label list, so remove the label from that list before dropping the statement subtree. */ if (scope != NULL) { a_label_ptr prev_label = NULL, curr_label; for (curr_label = scope->labels; curr_label != NULL; curr_label = curr_label->next) { if (curr_label == label) { if (prev_label == NULL) { scope->labels = curr_label->next; } else { prev_label->next = curr_label->next; } /* if */ if (scope->depth_in_scope_stack != NO_SCOPE_DEPTH && scope_stack[scope->depth_in_scope_stack].last_label == label) { scope_stack[scope->depth_in_scope_stack].last_label = prev_label; } /* if */ break; } /* if */ prev_label = curr_label; } /* for */ } /* if */ /* The symbol table entry may be needed by a later goto so that branching into the statement expression is diagnosed correctly. Leave it in place as a defined-but-eliminated label, but break the links from the label entry to the discarded statement subtree and from the label statement to its block-after-label lifetime. */ label->exec_stmt = NULL; label->next = NULL; stmt->variant.label.ptr = NULL; if (stmt->variant.label.lifetime != NULL) { unlink_object_lifetime(stmt->variant.label.lifetime); stmt->variant.label.lifetime = NULL; } /* if */ } /* if */ } /* remove_pragmas_associated_with_statement */ static void remove_statement_expr_pragmas_from_expr( an_expr_node_ptr expr, ARG_UNUSED an_expr_or_stmt_traversal_block_ptr tblock); static void remove_statement_expr_pragmas_from_constant( a_constant_ptr con, ARG_UNUSED an_expr_or_stmt_traversal_block_ptr tblock); static void remove_statement_expr_pragmas(a_statement_ptr stmt) /* Remove any pragmas associated with statements in the indicated GNU statement expression, which is being eliminated from the IL. */ { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_statement = remove_pragmas_associated_with_statement; tblock.process_expr = remove_statement_expr_pragmas_from_expr; tblock.process_constant = remove_statement_expr_pragmas_from_constant; tblock.process_non_dynamic_constants = TRUE; traverse_statement(stmt, &tblock); } /* remove_statement_expr_pragmas */ static void remove_statement_expr_pragmas_from_expr( an_expr_node_ptr expr, an_expr_or_stmt_traversal_block_ptr tblock) /* Called from traverse_expr to remove pragmas associated with statements in GNU statement expressions. */ { if (expr->kind == enk_statement) { remove_statement_expr_pragmas(expr->variant.statement); tblock->suppress_subtree_walk = TRUE; } /* if */ } /* remove_statement_expr_pragmas_from_expr */ static void remove_statement_expr_pragmas_from_constant( a_constant_ptr con, ARG_UNUSED an_expr_or_stmt_traversal_block_ptr tblock) /* Called from traverse_constant to remove pragmas associated with statements in GNU statement expressions in a folded constant's backing expression. */ { if (con->expr != NULL && con->folded_statement_expression) { eliminate_statement_expr_pragmas(con->expr); } /* if */ } /* remove_statement_expr_pragmas_from_constant */ void eliminate_statement_expr_pragmas(an_expr_node_ptr expr) /* Traverse expr looking for GNU statement expressions and remove any pragmas associated with statements they point to. tblock is ignored. */ { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = remove_statement_expr_pragmas_from_expr; tblock.process_constant = remove_statement_expr_pragmas_from_constant; tblock.process_non_dynamic_constants = TRUE; traverse_expr(expr, &tblock); } /* eliminate_statement_expr_pragmas */ #endif /* !STANDALONE_UTILITY_PROGRAM */ #if GENERATE_SOURCE_SEQUENCE_LISTS static void remove_statement_expr_src_seq_entries_from_constant( a_constant_ptr con, ARG_UNUSED an_expr_or_stmt_traversal_block_ptr tblock); static void remove_statement_expr_src_seq_entries( an_expr_node_ptr expr, ARG_UNUSED an_expr_or_stmt_traversal_block_ptr tblock) /* Called from traverse_expr to remove the source sequence entries associated with any statement expression. */ { if (node_is(expr, enk_statement)) { a_statement_ptr stmt = expr->variant.statement; a_source_sequence_entry_ptr head = stmt->source_sequence_entry; /* The statement expression's block scope is left on its parent's scopes list so that any IL entries reachable only through it (e.g., local class types or scopes in file-scope memory referenced via a_local_scope_ref entries) remain reachable for IL write/read. The source sequence entries spanning the statement expression are removed below, and the block scope itself does not have its own source_sequence_list. */ if (stmt->kind == stmk_block) { a_block_ptr block = stmt->variant.block.extra_info; a_scope_ptr scope = block->assoc_scope; #if !STANDALONE_UTILITY_PROGRAM remove_statement_expr_pragmas(stmt); #endif /* !STANDALONE_UTILITY_PROGRAM */ if (scope != NULL && scope->lifetime != NULL && !is_at_least_one_error()) { /* Error recovery may still need this lifetime while closing the enclosing control-flow blocks. */ unlink_object_lifetime(scope->lifetime); scope->lifetime = NULL; } /* if */ } /* if */ if (head != NULL) { a_source_sequence_entry_ptr tail = matching_end_of_construct(head); clear_src_seq_list_segment(head, tail->next); /* Remove the list of source sequence entries spanning this statement expression. */ remove_src_seq_list(head, tail); } else { an_expr_or_stmt_traversal_block statement_tblock; clear_expr_or_stmt_traversal_block(&statement_tblock); statement_tblock.process_statement = [](a_statement_ptr local_stmt, ARG_UNUSED an_expr_or_stmt_traversal_block_ptr) { if (local_stmt->source_sequence_entry != NULL) { remove_src_seq_entry(local_stmt->source_sequence_entry); } /* if */ }; traverse_statement(stmt, &statement_tblock); } /* if */ } /* if */ } /* remove_statement_expr_src_seq_entries */ static void remove_statement_expr_src_seq_entries_from_constant( a_constant_ptr con, ARG_UNUSED an_expr_or_stmt_traversal_block_ptr tblock) /* Called from traverse_constant to remove source sequence entries associated with statement expressions in a folded constant's backing expression. */ { if (con->expr != NULL && con->folded_statement_expression) { eliminate_statement_expr_src_seq_entries(con->expr); } /* if */ } /* remove_statement_expr_src_seq_entries_from_constant */ void eliminate_statement_expr_src_seq_entries(an_expr_node_ptr expr) /* Traverse expr for GNU statement expressions (which are about to be dropped from the IL) and eliminate any source sequence entries associated with the statements they point to. This function also removes the block scope associated with the statement expression from its parent's scopes list. */ { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = remove_statement_expr_src_seq_entries; tblock.process_constant = remove_statement_expr_src_seq_entries_from_constant; tblock.process_non_dynamic_constants = TRUE; traverse_expr(expr, &tblock); } /* eliminate_statement_expr_src_seq_entries */ #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ static void examine_type_for_instantiation_dependence( a_type_ptr type, an_expr_or_stmt_traversal_block_ptr tblock) /* Called from traverse_expr to check whether the type is instantiation-dependent. */ { if (is_instantiation_dependent_type(type)) { tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* examine_type_for_instantiation_dependence */ static void examine_var_init_for_instantiation_dependence( a_variable_ptr vp, an_expr_or_stmt_traversal_block_ptr tblock) /* Helper function to determine whether a given expression or constant is instantiation-dependent. Specifically, if the given variable is local to a function template, this function traverses the variable's initializer to see if it is instantiation-dependent. */ { if (vp->is_prototype_instantiation || (vp->source_corresp.enclosing_routine != NULL && vp->source_corresp.enclosing_routine->is_prototype_instantiation)) { an_init_kind init_kind, saved_init_kind = vp->init_kind; an_initializer_ptr initializer; get_variable_initializer(vp, (a_scope_ptr)NULL, &init_kind, &initializer); /* Temporarily disable the initializer to avoid unbounded recursion. */ vp->init_kind = (an_init_kind)initk_none; if (init_kind == (an_init_kind)initk_static) { traverse_constant(initializer->constant, tblock); } else if (init_kind == (an_init_kind)initk_dynamic) { traverse_dynamic_init(initializer->dynamic, tblock); } else if (init_kind == (an_init_kind)initk_binding) { traverse_expr(initializer->bound_expr, tblock); } /* if */ vp->init_kind = saved_init_kind; } /* if */ } /* examine_var_init_for_instantiation_dependence */ static void examine_constant_for_instantiation_dependence( a_constant_ptr con, an_expr_or_stmt_traversal_block_ptr tblock) /* Called from traverse_expr to check whether the constant is instantiation-dependent. */ { if (constant_is(con, ck_template_param) || con->is_generic_initializer) { tblock->result = TRUE; tblock->terminate = TRUE; } else if (constant_is(con, ck_address)) { if (con->variant.address.kind == (an_address_base_kind)abk_variable) { /* Check if the given variable's initializer makes it instantiation- dependent. */ a_variable *vp = con->variant.address.variant.variable; if (vp->source_corresp.is_class_member && parent_class_of(vp)->variant.class_struct_union.is_nonreal_class) { tblock->result = TRUE; tblock->terminate = TRUE; } else { examine_var_init_for_instantiation_dependence(vp, tblock); } /* if */ } /* if */ tblock->suppress_subtree_walk = TRUE; } else if (constant_is(con, ck_aggregate) || constant_is(con, ck_dynamic_init) || constant_is(con, ck_init_repeat)) { /* Aggregate constants can contain dynamic members, which might be instantiation-dependent, so walk the subtree for those. */ } else { /* Other constants are known not to be dependent. */ tblock->suppress_subtree_walk = TRUE; } /* if */ } /* examine_constant_for_instantiation_dependence */ a_boolean expr_is_dep_static_member_of_current_instantiation( an_expr_node_ptr expr) /* Return TRUE if the given expression is a routine node for the address of a static member of the current template instantiation, which is considered dependent. */ { a_boolean is_dependent = FALSE; expr = skip_parens(expr); if (is_routine_node(expr)) { a_routine_ptr rout = node_routine(expr); if (rout != NULL && rout->source_corresp.is_class_member && !routine_type_is_nonstatic_member_function(rout->type) && parent_class_of(rout)->variant.class_struct_union.is_nonreal_class) { is_dependent = TRUE; } /* if */ } /* if */ return is_dependent; } /* expr_is_dep_static_member_of_current_instantiation */ static void examine_expr_for_instantiation_dependence( an_expr_node_ptr expr, an_expr_or_stmt_traversal_block_ptr tblock) /* Called from traverse_expr to check whether the expression is instantiation-dependent. */ { a_boolean skip_typecheck = FALSE; /* Most cases are resolved by the type, but a routine node that is the address of a static member of the current instantiation is not. */ if (expr_is_dep_static_member_of_current_instantiation(expr)) { tblock->result = TRUE; tblock->terminate = TRUE; } else if (is_variable_node(expr)) { /* Treat local variables of function templates as "instantiation dependent". */ a_variable *vp = node_variable(expr); if (vp->is_nonreal) { tblock->result = TRUE; tblock->terminate = TRUE; } else { examine_var_init_for_instantiation_dependence(vp, tblock); } /* if */ } else if (node_is(expr, enk_type_operand)) { /* enk_type_operand nodes have "void" type, but the instantiation dependence is in the operand type. */ examine_type_for_instantiation_dependence(expr->variant.type_operand.type, tblock); skip_typecheck = TRUE; } /* if */ if (skip_typecheck || tblock->terminate) { /* No need to check the type. */ } else { examine_type_for_instantiation_dependence(expr->type, tblock); } /* if */ } /* examine_expr_for_instantiation_dependence */ static void examine_dyn_init_for_instantiation_dependence( a_dynamic_init_ptr dip, ARG_UNUSED an_expr_or_stmt_traversal_block_ptr tblock) /* Check if the given dynamic initializer is instantiation-dependent. Called indirectly by expr_is_instantiation_dependent. */ { if (dyn_init_is(dip, dik_constructor)) { if (dip->variant.constructor.ptr == NULL) { /* An unknown constructor only occurs when it is template-dependent. */ tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* if */ } /* examine_dyn_init_for_instantiation_dependence */ a_boolean expr_is_instantiation_dependent(an_expr_node_ptr expr) /* Return TRUE if expr is instantiation-dependent. This normally includes type- dependent and value-dependent cases, and also cases where a template parameter appears in a subexpression but the result is neither type-dependent or value- dependent (e.g. "sizeof(sizeof(T))"). */ { a_boolean result = FALSE; if (!C_mode()) { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = examine_expr_for_instantiation_dependence; tblock.process_constant = examine_constant_for_instantiation_dependence; tblock.process_dynamic_init = examine_dyn_init_for_instantiation_dependence; tblock.process_type = examine_type_for_instantiation_dependence; tblock.process_non_dynamic_constants = TRUE; tblock.skip_expr_process_type = TRUE; traverse_expr(expr, &tblock); result = tblock.result; } /* if */ return result; } /* expr_is_instantiation_dependent */ a_boolean constant_is_instantiation_dependent(a_constant_ptr con) /* Return TRUE if con is instantiation-dependent. This includes type-dependent and value-dependent cases, and also cases where a template parameter appears in a subexpression but the result is neither type-dependent or value-dependent. */ { a_boolean result = FALSE; if (!C_mode()) { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_constant = examine_constant_for_instantiation_dependence; tblock.process_type = examine_type_for_instantiation_dependence; tblock.process_non_dynamic_constants = TRUE; traverse_constant(con, &tblock); result = tblock.result; } /* if */ return result; } /* constant_is_instantiation_dependent */ static void examine_constant_for_local_var_address( a_constant_ptr con, an_expr_or_stmt_traversal_block_ptr tblock) /* Called from traverse_constant to check whether the constant contains a ck_address entry referring to a local variable. */ { switch (con->kind) { case ck_address: if (address_base_is(con, abk_variable)) { if (con->variant.address.variant.variable->storage_class == sc_auto) { tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* if */ /* Be sure to avoid following pointers, since constants can be self-referential in that way. */ tblock->suppress_subtree_walk = TRUE; break; case ck_dynamic_init: case ck_template_param: case ck_reflection: tblock->suppress_subtree_walk = TRUE; break; default: break; } /* switch */ } /* examine_constant_for_local_var_address */ a_boolean constant_addresses_local_var(a_constant_ptr con) /* Return TRUE if con contains a ck_address component referring to a local variable. */ { a_boolean result = FALSE; switch (con->kind) { case ck_address: case ck_aggregate: { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_constant = examine_constant_for_local_var_address; tblock.process_non_dynamic_constants = TRUE; traverse_constant(con, &tblock); result = tblock.result; } break; default: break; } /* switch */ return result; } /* constant_addresses_local_var */ static void examine_type_for_error(a_type_ptr type, an_expr_or_stmt_traversal_block_ptr tblock) /* Called from traverse_expr to check whether the type is or contains an error type. */ { if (is_or_contains_error_type(type)) { tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* examine_type_for_error */ static void examine_constant_for_error( a_constant_ptr con, an_expr_or_stmt_traversal_block_ptr tblock) /* Called from traverse_expr to check whether the constant is an error constant. */ { if (is_error_constant(con)) { tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* examine_constant_for_error */ static void examine_expr_for_error(an_expr_node_ptr expr, an_expr_or_stmt_traversal_block_ptr tblock) /* Called from traverse_expr to check whether the expression is an error node. */ { if (is_error_node(expr)) { tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* examine_expr_for_error */ a_boolean expr_contains_error(an_expr_node_ptr expr) /* Return TRUE if expr contains an error node, error constant, or error type. */ { a_boolean result = FALSE; if (!C_mode()) { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = examine_expr_for_error; tblock.process_constant = examine_constant_for_error; tblock.process_type = examine_type_for_error; tblock.process_non_dynamic_constants = TRUE; tblock.process_expressions_for_constants = TRUE; tblock.process_template_parameter_constants_and_expressions = TRUE; traverse_expr(expr, &tblock); result = tblock.result; } /* if */ return result; } /* expr_contains_error */ a_boolean constant_contains_error(a_constant_ptr con) /* Return TRUE if con contains an error constant, error node, or error type (including in expressions in its subtree). */ { a_boolean result = FALSE; if (!C_mode()) { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = examine_expr_for_error; tblock.process_constant = examine_constant_for_error; tblock.process_type = examine_type_for_error; tblock.process_non_dynamic_constants = TRUE; tblock.process_expressions_for_constants = TRUE; tblock.process_template_parameter_constants_and_expressions = TRUE; traverse_constant(con, &tblock); result = tblock.result; } /* if */ return result; } /* constant_contains_error */ #if IA64_ABI static a_boolean generated_copy_constructor_is_trivial( a_class_symbol_supplement_ptr cssp) /* Return TRUE if the class associated with cssp has a trivial generated copy constructor. */ { a_boolean result = FALSE; a_symbol_ptr sym = cssp->constructor; if (symbol_is(sym, sk_overloaded_function)) { sym = sym->variant.overloaded_function.symbols; for (; sym != NULL; sym = sym->next) { if (symbol_is(sym, sk_member_function) && sym->variant.routine.ptr->is_trivial_copy_function) { result = TRUE; break; } /* if */ } /* if */ } else if (symbol_is(sym, sk_routine) && sym->variant.routine.ptr->is_trivial_copy_function) { result = TRUE; } /* if */ return result; } /* generated_copy_constructor_is_trivial */ #endif /* IA64_ABI */ a_boolean type_returned_by_cctor(a_type_ptr return_type, a_boolean *p_incomplete) /* Return TRUE if the given return type requires the caller to provide a temporary in which the callee constructs the result. Otherwise, return FALSE and if the type is incomplete but not template-dependent, set *p_incomplete to TRUE so that the caller may record a fixup entry to revisit the transfer method later. (This routine is also used for the "return" type of GNU statement expressions.) */ { a_boolean result = FALSE, incomplete = FALSE; return_type = skip_typerefs(return_type); if (is_immediate_class_type(return_type)) { a_class_symbol_supplement_ptr cssp; cssp = class_symbol_supp(symbol_for(return_type)); if (return_type->variant.class_struct_union.is_nonreal_class) { /* For a nonreal class, we can't answer the question, so leave the flag FALSE. */ } else if (return_type->incomplete) { incomplete = TRUE; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (cli_or_cx_enabled && is_cli_interface_type(return_type)) { /* C++/CLI interface types cannot be return types. This is diagnosed elsewhere. */ expect_error(); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else if (!(cssp->construction_by_bitwise_copy_allowed #if IA64_ABI || (emulate_gnu_abi_bugs && gnu_abi_version < 40600 && generated_copy_constructor_is_trivial(cssp)) #endif /* IA64_ABI */ ) || cssp->has_deleted_copy_or_move_constructor #if IA64_ABI #if ABI_COMPATIBILITY_VERSION >= 408 || has_deleted_or_nontrivial_destructor(cssp) #else /* ABI_COMPATIBILITY_VERSION < 408 */ /* This test had failed to take into account defaulted destructors and is replaced by the test above, but remains here for backward compatibility. */ || cssp->destructor != NULL #endif /* ABI_COMPATIBILITY_VERSION >= 408 */ #endif /* IA64_ABI */ ) { result = TRUE; } /* if */ } /* if */ *p_incomplete = incomplete; return result; } /* type_returned_by_cctor */ void set_routine_calling_method_flag(a_type_ptr routine_type, a_source_position *err_pos) /* Set the calling-method flag in the indicated routine type; that flag is used when the function result is returned to a temporary provided by the caller. Also issue a diagnostic if the return type is an abstract class. This routine may be called more than once, since the information on the return type can be incomplete at the original declaration of the function and must be completed by the point of call. Note: one cannot assume that a routine with a body has its flag set correctly; e.g., this may not be the case if the return type was incomplete at the point of definition. */ { a_routine_type_supplement_ptr rtsp; a_type_ptr return_type; db_enter(4, "set_routine_calling_method_flag"); if (C_dialect != C_dialect_cplusplus) { /* The flag cannot be set in C mode. */ } else { routine_type = skip_typerefs(routine_type); rtsp = routine_type->variant.routine.extra_info; if (rtsp->value_returned_by_cctor) { /* Once the flag is set, it will never change. */ } else { /* If the function returns a class object that has a "real" copy constructor, make the caller provide a temporary for the result. */ a_boolean incomplete; return_type = skip_typerefs(routine_type->variant.routine.return_type); if (type_returned_by_cctor(return_type, &incomplete)) { rtsp->value_returned_by_cctor = TRUE; /* If the return type is an abstract class, issue an error. Note that construction_by_bitwise_copy_allowed will never be TRUE for abstract classes. Also note that this logic assumes that value_returned_by_cctor will never be set elsewhere. */ if (return_type->variant.class_struct_union.abstract) { if (err_pos->seq == 0 || relaxed_abstract_checking || gpp_version_is(<50000) || clang_mode) { /* A null error position indicates a routine type for which there is no corresponding source position -- e.g., a type is being copied for some reason. Issue no diagnostic in such cases. If relaxed abstract checking (P0929R2) checking is in effect, we only diagnose abstract class return types when the function is defined or called, not, as here, when it is merely declared. Early versions of g++ did not check for abstract return types at all. Clang does not check for abstract return types on member functions, including static member functions, although it does do so for nonmember functions. We cannot tell here whether a function type is from a static member function or a nonmember function, so we defer the check in clang mode to scan_nonmember_declaration. */ } else { abstract_class_diagnostic( es_error, ec_function_returning_abstract_class, return_type, err_pos); } /* if */ } /* if */ } else if (incomplete) { /* The return type is an incomplete class so we can't tell whether special handling will be required for the return. Enter the routine type on a fixup list and check again when the return type has been defined. */ add_to_dependent_type_fixup_list(return_type, (a_dependent_type_fixup_kind) dtfk_routine_calling_method, (char *)routine_type, iek_type, err_pos); } /* if */ } /* if */ } /* if */ db_exit(); } /* set_routine_calling_method_flag */ a_boolean routine_has_default_args(a_routine_ptr rp) /* Return TRUE if the given routine has one or more default arguments. */ { a_boolean result = FALSE; a_type_ptr rtp = skip_typerefs(rp->type); a_param_type_ptr ptp = function_type_params(rtp); for (; ptp != NULL; ptp = ptp->next) { if (ptp->has_default_arg) { result = TRUE; break; } /* if */ } /* for */ return result; } /* routine_has_default_args */ a_boolean has_uninstantiated_default_arg(a_type_ptr rtp) /* Return TRUE if the given type is a function type with at least one parameter that has an uninstantiated default argument. */ { a_boolean result = FALSE; if (type_is(rtp, tk_routine)) { a_param_type_ptr ptp = function_type_params(rtp); for (; ptp != NULL; ptp = ptp->next) { if (ptp->has_default_arg && ptp->default_arg_expr == NULL) { result = TRUE; break; } /* if */ } /* for */ } /* if */ return result; } /* has_uninstantiated_default_arg */ void mark_routine_referenced_full(a_routine_ptr routine, a_boolean instantiate, a_boolean elided_reference) /* Mark the indicated routine as actually referenced. "Actually" means as opposed to referenced in a virtual function call that may call some other virtual function. Note that an actual reference does not necessarily mean that the routine is called; this reference might be taking the address of the routine. It does, however, force instantiation if the function is a template function, and/or definition if the function is the right kind of compiler-generated function (e.g., a constructor). Instantiation is forced only if instantiate is TRUE. If elided_reference is TRUE, the reference has been elided by copy constructor elision; do not mark the routine as actually referenced. */ { a_symbol_ptr assoc_sym; /* We need to make sure templates with lazily loaded definitions are completely loaded before marking the template referenced. This ensures a template's specializations are not processed after the template is referenced (resulting in a spurious diagnostic). */ a_template_ptr templ = routine->assoc_template; if (templ != NULL) { if (has_pending_template_definition_from_module(templ)) { (void)load_template_definition_from_module(templ); } else if (has_pending_template_specializations_from_module(templ)) { (void)load_template_specializations_from_module(templ); } /* if */ } /* if */ if (!elided_reference) { /* Set the referenced flag. This is only necessary for virtual functions referenced by qualified name. For non-virtual functions, the normal reference-processing routines have already set the IL referenced flag. */ routine->source_corresp.referenced = TRUE; /* If the routine is a nonstatic member function, mark the class of which it is a member as referenced. This ensures that a class will not end up marked as unreferenced when one of its nonstatic member functions (which references the class at least in its "this" parameter) is marked referenced. */ if (routine_type_is_nonstatic_member_function(routine->type)) { parent_class_of(routine)->source_corresp.referenced = TRUE; } /* if */ #if USE_X86_FUNCTION_MULTIVERSIONING if (gpp_mode && is_multiversion_representative(routine)) { /* Record a reference to a GNU function multiversion member function. */ reference_to_mv_routine(routine, (a_source_position*)NULL); } /* if */ #endif /* USE_X86_FUNCTION_MULTIVERSIONING */ } /* if */ /* If the routine is compiler-generated and its definition has not yet been put out, force the definition now. */ force_definition_of_compiler_generated_routine(routine); /* In some modes, friend functions defined in class templates are only analyzed if they are used. A non-NULL routine_fixup pointer indicates that the definition has not yet been processed. This special treatment is also extended to explicit specializations that are defined within a class. */ if (routine->routine_fixup != NULL) { add_to_deferred_friend_function_fixup_list(routine->routine_fixup); } /* if */ if (!is_context_where_instance_is_needed()) { /* Do not instantiate things referenced from dependent contexts, etc. */ instantiate = FALSE; } else if (instantiate && translation_unit_needed_only_for_exported_templates) { /* In a secondary translation unit that is being used only for exported templates, the bodies of noninline external functions are discarded. If this reference is from such a routine, do not mark any functions called as required for instantiation purposes. */ if (is_nonspecialized_instantiation_context()) { /* This reference is from within a template. Record this reference. */ } else if (depth_innermost_function_scope != NO_SCOPE_DEPTH) { /* We are processing the body of a nontemplate function. Do not record the reference unless the function is inline. */ a_routine_ptr curr_routine; curr_routine = scope_stack[depth_innermost_function_scope].assoc_routine; check_assertion(curr_routine != NULL); if (!curr_routine->is_inline) instantiate = FALSE; } /* if */ } /* if */ if (instantiate && routine->is_inline && !routine->defined && !routine->is_consteval) { /* If an imported inline function is referenced in a context that would require instantiation, it would also require being loaded from the module file. In the case of consteval functions, that can be postponed until the constant-evaluator needs the function definition. */ if (has_routine_definition_from_module(routine)) { (void)load_routine_definition_from_module(routine); } /* if */ } /* if */ /* If the function is an instance of a function template, mark it as requiring an instantiation. This is also done for extern inline functions when inline functions are instantiated using a mechanism like the template instantiation mechanism. */ assoc_sym = symbol_for(routine); if (instantiate && assoc_sym != NULL) { set_instance_required(assoc_sym, TRUE, SIR_NONE); } /* if */ if (assoc_sym != NULL) { /* Make sure the exception specification of the routine, if any, has been instantiated. */ instantiate_exception_spec_if_needed(assoc_sym); } /* if */ } /* mark_routine_referenced_full */ void mark_routine_referenced(a_routine_ptr routine) /* Interface to mark_routine_referenced_full for the simple case. */ { mark_routine_referenced_full(routine, /*instantiate=*/TRUE, /*elided_reference=*/FALSE); } /* mark_routine_referenced */ void set_routine_defined(a_routine_ptr rout) /* Set the "defined" flag for a routine. This can kick off some processing related to "needed" flags. */ { if (!rout->defined) { #if GNU_EXTENSIONS_ALLOWED && !LOWER_IFUNC /* An ifunc shouldn't have a definition (unless lowering gives it one). */ check_assertion(!rout->is_ifunc || is_at_least_one_error()); #endif /* GNU_EXTENSIONS_ALLOWED && !LOWER_IFUNC */ rout->defined = TRUE; #if MAINTAIN_NEEDED_FLAGS #if DEBUG if (db_trace("needed_flags", rout, iek_routine)) { fprintf(f_debug, "Setting defined on rout "); db_name_full(&rout->source_corresp, iek_routine); fprintf(f_debug, "\n"); } /* if */ #endif /* DEBUG */ /* If the definition_needed flag was set previously, set it again so the function body will be scanned. The fact that rout->defined was FALSE prevented the scanning of the body. Likewise for the keep_definition_in_il flag. */ remark_routine_definition_needed(rout); #endif /* MAINTAIN_NEEDED_FLAGS */ } /* if */ } /* set_routine_defined */ a_statement_ptr make_assignment_statement(an_expr_node_ptr dest, an_expr_node_ptr source) /* Create an expression statement pointing to an assignment operator that assigns the rvalue "source" to the lvalue "dest". Return a pointer to the statement. May not be used for array types. */ { a_statement_ptr stmt; an_expr_node_ptr node; check_assertion(dest->is_lvalue || is_error_node(dest)); check_assertion(!is_glvalue_node(source) || is_error_node(source)); /* Make the assignment node. */ dest->next = source; node = make_operator_node(which_binary_operator(tok_assign, source->type), source->type, dest); if (strict_cpp17_eval_order) { node->variant.operation.eval_right_to_left = TRUE; } /* if */ /* Allocate the statement. */ stmt = alloc_expr_statement(node); return stmt; } /* make_assignment_statement */ a_statement_ptr make_array_assignment_statement(an_expr_node_ptr dest, an_expr_node_ptr source) /* Create an expression statement pointing to an assignment operator that assigns the array lvalue "source" to the lvalue "dest". Return a pointer to the statement. This doesn't come up directly in programs, but does in IL lowering and in generated routines (like assignment operator functions). */ { a_statement_ptr stmt; an_expr_node_ptr node; check_assertion(dest->is_lvalue || is_error_node(dest)); check_assertion((source->is_lvalue || is_error_node(source)) && (is_array_type(source->type) || is_error_type(source->type))); /* Make the assignment node. */ dest->next = source; node = make_operator_node((an_expr_operator_kind)eok_bassign, void_type(), dest); if (strict_cpp17_eval_order) { node->variant.operation.eval_right_to_left = TRUE; } /* if */ /* Allocate the statement. */ stmt = alloc_expr_statement(node); return stmt; } /* make_array_assignment_statement */ void set_block_scope_handler(a_handler_ptr handler) /* Set the assoc_handler field of the current scope. */ { a_scope_ptr sp; sp = ensure_il_scope_exists(&scope_stack[decl_scope_level]); check_assertion_str(sp != NULL, "set_block_scope_handler: NULL IL scope"); sp->variant.assoc_handler = handler; } /* set_block_scope_handler */ a_statement_ptr alloc_expr_statement(an_expr_node_ptr node) /* Allocate an stmk_expr statement pointing to the indicated expression and return a pointer to it. This routine assumes that the statement being added is compiler-generated. */ { a_statement_ptr stmt = alloc_statement(stmk_expr, /*compiler_generated=*/TRUE); stmt->expr = node; set_expr_result_not_used(node); return stmt; } /* alloc_expr_statement */ void copy_statement(a_statement *from, a_statement *to) /* Copy a statement entry from "from" to "to", preserving certain fields of the original statement. */ { a_boolean has_associated_pragma = to->has_associated_pragma; a_boolean compiler_generated = to->compiler_generated; #if DO_IL_LOWERING a_boolean lowering_generated = to->lowering_generated; #endif /* DO_IL_LOWERING */ a_statement_ptr to_next = to->next; *to = *from; /* Preserve some of the fields from the destination statement. */ to->next = to_next; to->has_associated_pragma = has_associated_pragma; to->compiler_generated = compiler_generated; #if DO_IL_LOWERING to->lowering_generated = lowering_generated; #endif /* DO_IL_LOWERING */ /* Patch up subordinate parent pointers and similar "back" pointers. */ switch (to->kind) { case stmk_if: case stmk_if_consteval: case stmk_if_not_consteval: /* Update the "then" and "else" (if present) branch parents. */ to->variant.if_stmt.then_statement->parent = to; if (to->variant.if_stmt.else_statement != NULL) { to->variant.if_stmt.else_statement->parent = to; } /* if */ break; case stmk_constexpr_if: /* Update the "then" and "else" (if present) branch parents. */ to->variant.constexpr_if->then_statement->parent = to; if (to->variant.constexpr_if->else_statement != NULL) { to->variant.constexpr_if->else_statement->parent = to; } /* if */ break; case stmk_while: case stmk_end_test_while: to->variant.loop_statement->parent = to; break; case stmk_label: /* If the statement is a label, bind the a_label to the copy. */ to->variant.label.ptr->exec_stmt = to; break; case stmk_block: /* If the statement is a block with an associated scope, change the back-pointer from the scope to point to the copy. */ { a_scope_ptr scope = to->variant.block.extra_info->assoc_scope; a_statement_ptr sp = to->variant.block.statements; if (scope != NULL) scope->assoc_block = to; for (; sp != NULL; sp = sp->next) { sp->parent = to; } /* if */ /* To be neat: */ from->variant.block.statements = NULL; } /* if */ break; case stmk_for: #if UPC_EXTENSIONS_ALLOWED case stmk_upc_forall: #endif /* UPC_EXTENSIONS_ALLOWED */ to->variant.for_loop.statement->parent = to; if (to->variant.for_loop.extra_info->initialization != NULL) { to->variant.for_loop.extra_info->initialization->parent = to; } /* if */ break; case stmk_range_based_for: to->variant.range_based_for_loop.statement->parent = to; break; case stmk_switch: /* If the statement is a switch statement, make sure all of the case statements are modified to point to the new switch statement. */ { a_switch_case_entry_ptr case_entry; for (case_entry = to->variant.switch_stmt.extra_info->cases; case_entry != NULL; case_entry = case_entry->next) { check_assertion( case_entry->stmt->variant.switch_case.switch_statement == from); case_entry->stmt->variant.switch_case.switch_statement = to; } /* for */ to->variant.switch_stmt.body_statement->parent = to; } break; case stmk_switch_case: /* Modify the pointer in a switch case statement to point to the copy. */ check_assertion(to->variant.switch_case.extra_info->stmt == from); to->variant.switch_case.extra_info->stmt = to; break; case stmk_try_block: { a_try_supplement_ptr tsp = to->variant.try_block; a_handler_ptr hp; tsp->statement->parent = to; for (hp = tsp->handlers; hp != NULL; hp = hp->next) { hp->statement->parent = to; } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (tsp->finally_statement != NULL) { tsp->finally_statement->parent = to; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } break; #if MICROSOFT_EXTENSIONS_ALLOWED case stmk_microsoft_try: { a_microsoft_try_supplement_ptr mtsp = to->variant.microsoft_try; mtsp->guarded_statement->parent = to; mtsp->cleanup_statement->parent = to; } break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ default: /* Nothing to do. */ ; } /* switch */ } /* copy_statement */ void change_statement_into_block(a_statement_ptr statement, a_statement_ptr *orig_statement) /* Turn a statement into a block by allocating a new statement, copying the statement to it, and changing the original statement into a block containing the copied statement. *orig_statement is set to point to the original statement in its new location. Note that changing a stmk_stmt_expr_result statement into a block (typically only done during lowering) requires special handling (because that statement is the only statement that returns a value). See change_block_into_statement_expression. */ { a_statement_ptr stmt_copy; /* Make a copy of the original statement. */ *orig_statement = stmt_copy = alloc_statement(statement->kind, /*compiler_generated=*/TRUE); copy_statement(statement, stmt_copy); /* Turn the statement into a block statement. */ set_statement_kind(statement, (a_statement_kind)stmk_block); statement->variant.block.statements = stmt_copy; stmt_copy->parent = statement; statement->position = null_source_position; #if EXTRA_SOURCE_POSITIONS_IN_IL statement->end_position = null_source_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* change_statement_into_block */ #endif /* !STANDALONE_UTILITY_PROGRAM */ static inline void find_scope_for_entity_pragmas( char *entity_ptr, an_il_entry_kind entity_kind, a_source_correspondence_ptr entity_scp, a_scope_ptr func_or_templ_scope, a_scope_ptr *il_scope, a_scope_pointers_block_ptr *pointers_block) /* Given an IL entity pointer, kind, and the associated source correspondence for the entity: this routine determines the IL scope (*il_scope) and associated pointers (*pointers_block) block (if any) that list the pragmas for the given entity. If called with a function-local entity, func_or_templ_scope must be set to the associated innermost_function_scope (or in the case of a function prototype, the scope associated with depth_template_declaration_scope). Otherwise, func_or_templ_scope scope can be NULL. */ { *il_scope = NULL; *pointers_block = NULL; if (entity_scp != NULL && in_file_scope(entity_scp)) { /* When an entity source correspondence is present and the entity is not function local, use get_scope_for_list to determine an appropriate scope. */ *il_scope = get_scope_for_list(NO_SCOPE_DEPTH, entity_scp, pointers_block); } else if (entity_kind == iek_statement && in_file_scope(entity_ptr)) { /* Statement expressions in unevaluated contexts can produce statements allocated in file-scope memory even while a function scope is active. Currently, this is only known to occur in C mode. */ *il_scope = scope_stack[depth_innermost_namespace_scope].il_scope; } else if (entity_kind == iek_statement || entity_scp != NULL) { /* This is a pragma applied to either a statement or function local entity. These pragmas are associated with the innermost associated function scope. */ if (func_or_templ_scope == NULL) { /* GNU statement expressions in file or namespace scopes are stored in the file scope. All other statements and entities should have a scope set. The presence of a GNU statement expression is only checked in the front end as a sanity check (when find_scope_for_entity_pragmas is called as part of add_entity_pragma_to_list). */ #if !STANDALONE_UTILITY_PROGRAM check_assertion(!in_front_end || in_gnu_stmt_expression()); #endif /* !STANDALONE_UTILITY_PROGRAM */ *il_scope = scope_stack[DEPTH_OF_FILE_SCOPE].il_scope; } else { *il_scope = func_or_templ_scope; } /* if */ if (!scope_is_null_or_placeholder(*il_scope) && (*il_scope)->depth_in_scope_stack != NO_SCOPE_DEPTH) { /* There is a corresponding scope stack entry: update the pointers block value. */ *pointers_block = scope_stack[(*il_scope)->depth_in_scope_stack].assoc_pointers_block; } /* if */ } /* if */ /* There should always be an IL scope at this point. */ check_assertion(*il_scope != NULL); } /* find_scope_for_entity_pragmas */ #if !STANDALONE_UTILITY_PROGRAM static void append_pragma(a_scope_ptr il_scope, a_scope_pointers_block_ptr pointers_block, a_pragma_ptr pp) /* Append the given pragma pointer (pp) to the given il_scope (using pointers_block as an optimization if non-NULL). */ { if (il_scope->pragmas == NULL) { il_scope->pragmas = pp; } else if (pointers_block == NULL) { /* No scope stack entry, find the end of the pragma list. Note that the case where sp->pragmas is NULL is already tested above. */ a_pragma_ptr end_of_list = il_scope->pragmas; while (end_of_list->next != NULL) { end_of_list = end_of_list->next; } /* while */ end_of_list->next = pp; } else { pointers_block->last_pragma->next = pp; } /* if */ if (pointers_block != NULL) { pointers_block->last_pragma = pp; } /* if */ } /* append_pragma */ a_pragma_ptr add_non_entity_pragma_to_list( a_pragma_kind_description_ptr pragma_descr, a_source_position pragma_position, char *pragma_text, #if MICROSOFT_EXTENSIONS_ALLOWED a_boolean is_microsoft_pragma_operator, #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ a_scope_depth pragma_scope_depth) /* This function constructs a new IL pragma entry that's not associated with any IL entity using the given pragma_descr (the pragma description), pragma_position (the pragma source position), pragma_text (the text that comprises the pragma), and is_microsoft_pragma_operator (if the pragma is a Microsoft pragma operator). Then, the constructed pragma is added to the appropriate scope on the scope stack (as specified by pragma_scope_depth). */ { a_memory_region_number region_to_switch_back_to = NO_MEMORY_REGION_NUMBER; switch_to_scope_region(pragma_scope_depth, ®ion_to_switch_back_to); a_pragma_ptr pp = alloc_pragma(pragma_descr->kind); pp->position = pragma_position; pp->pragma_text = pragma_text; pp->ignore_in_back_end = pragma_descr->ignore_in_back_end; #if MICROSOFT_EXTENSIONS_ALLOWED pp->is_microsoft_pragma_operator = is_microsoft_pragma_operator; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if DEBUG if (db_flag_is_set("add_pragma_to_il")) { fprintf(f_debug, "Adding non-entity pragma at seq=%u, col=%u to scope %d " "(depth %d)\n", pp->position.seq, (unsigned)pp->position.column, scope_stack[pragma_scope_depth].number, pragma_scope_depth); } /* if */ #endif /* DEBUG */ append_pragma(scope_stack[pragma_scope_depth].il_scope, scope_stack[pragma_scope_depth].assoc_pointers_block, pp); if (region_to_switch_back_to != NO_MEMORY_REGION_NUMBER) { switch_back_to_original_region(region_to_switch_back_to); } /* if */ return pp; } /* add_non_entity_pragma_to_list */ a_pragma_ptr add_entity_pragma_to_list( a_pragma_kind_description_ptr pragma_descr, a_source_position pragma_position, char *pragma_text, #if MICROSOFT_EXTENSIONS_ALLOWED a_boolean is_microsoft_pragma_operator, #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ char *entity_ptr, an_il_entry_kind entity_kind) /* This function constructs a new IL pragma entry that's associated with an IL entity using the given pragma_descr (the pragma description), pragma_position (the pragma source position), pragma_text (the text that comprises the pragma), is_microsoft_pragma_operator (if the pragma is a Microsoft pragma operator), and associated IL entity. Then, the constructed pragma is added to the appropriate scope so that it can be retrieved by find_assoc_pragma later. Note that if called with a function-local entity, the scope stack for get_depth_innermost_function_scope() and depth_template_declaration_scope must be set appropriately. */ { a_boolean tu_pushed = FALSE; a_memory_region_number region_to_switch_back_to = NO_MEMORY_REGION_NUMBER; a_source_correspondence_ptr scp = source_corresp_for_il_entry(entity_ptr, entity_kind); if (scp != NULL) { a_symbol_ptr sym = (a_symbol_ptr)scp->assoc_info; tu_pushed = push_translation_unit_if_needed(sym); } /* if */ a_scope_ptr il_scope; a_scope_pointers_block_ptr pointers_block; a_scope_ptr scope_for_function_local = NULL; a_scope_depth func_scope_depth = get_depth_innermost_function_scope(); if (func_scope_depth != NO_SCOPE_DEPTH) { scope_for_function_local = scope_stack[func_scope_depth].il_scope; } else if (is_template_declaration_context()) { check_assertion(depth_template_declaration_scope != NO_SCOPE_DEPTH); scope_for_function_local = scope_stack[depth_template_declaration_scope].il_scope; } /* if */ find_scope_for_entity_pragmas(entity_ptr, entity_kind, scp, scope_for_function_local, &il_scope, &pointers_block); if (il_scope->depth_in_scope_stack != NO_SCOPE_DEPTH) { switch_to_scope_region(il_scope->depth_in_scope_stack, ®ion_to_switch_back_to); } else { switch_to_file_scope_region(®ion_to_switch_back_to); } /* if */ /* Set the has_associated_pragma field. */ if (scp != NULL) { scp->has_associated_pragma = TRUE; } else if (entity_kind == iek_statement) { ((a_statement_ptr)entity_ptr)->has_associated_pragma = TRUE; } /* if */ a_pragma_ptr pp = alloc_pragma(pragma_descr->kind); pp->position = pragma_position; pp->pragma_text = pragma_text; pp->ignore_in_back_end = pragma_descr->ignore_in_back_end; #if MICROSOFT_EXTENSIONS_ALLOWED pp->is_microsoft_pragma_operator = is_microsoft_pragma_operator; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ pp->entity.kind = entity_kind; pp->entity.ptr = entity_ptr; #if DEBUG if (db_flag_is_set("add_pragma_to_il")) { fprintf(f_debug, "Adding entity pragma at seq=%u, col=%u to scope %d " "(depth %d)\n", pp->position.seq, (unsigned)pp->position.column, il_scope->number, il_scope->depth_in_scope_stack); } /* if */ #endif /* DEBUG */ append_pragma(il_scope, pointers_block, pp); if (region_to_switch_back_to != NO_MEMORY_REGION_NUMBER) { switch_back_to_original_region(region_to_switch_back_to); } /* if */ if (tu_pushed) { pop_translation_unit_stack(); } /* if */ return pp; } /* add_entity_pragma_to_list */ #endif /* !STANDALONE_UTILITY_PROGRAM */ a_pragma_ptr find_assoc_pragma(char *entity_ptr, an_il_entry_kind entity_kind, a_scope_ptr func_or_templ_scope, a_pragma_ptr prev_assoc_pragma) /* Return a pointer to a pragma that is bound to the IL entity described by entity_ptr and entity_kind. prev_assoc_pragma is a (possibly NULL) pointer to another pragma that is bound to the same IL entity and has already been located (used for iterative calls to find_assoc_pragma and is typically the result of the previous (non-NULL) find_assoc_pragma call with the same il_entity). When prev_assoc_pragma is NULL, the list of pragmas where any new pragmas for the given entity would be added is searched. To search for pragmas associated with entities that are function-local, func_or_templ_scope must be set to the innermost function scope or (if searching for a pragma associated with a prototype instantiation) the template declaration scope that would've been active when the pragma was created. When prev_assoc_pragma is non-NULL, search the remainder of the list it belongs to. The front end aborts with a failed assertion if prev_assoc_pragma is NULL and no pragma is found (i.e., the caller should ensure that at least one pragma is bound to the il_entity). */ { a_pragma_ptr assoc_pragma; if (prev_assoc_pragma != NULL) { /* A pragma has already been found that is associated with *il_entity. Any additional pragmas associated with the same entity will be among its successors on the same list. */ assoc_pragma = prev_assoc_pragma->next; } else { /* Determine which scope has the list that is to be searched. */ a_source_correspondence_ptr scp = source_corresp_for_il_entry(entity_ptr, entity_kind); a_scope_pointers_block_ptr pointers_block; a_scope_ptr il_scope; find_scope_for_entity_pragmas(entity_ptr, entity_kind, scp, func_or_templ_scope, &il_scope, &pointers_block); assoc_pragma = il_scope->pragmas; } /* if */ for (; assoc_pragma != NULL; assoc_pragma = assoc_pragma->next) { if (assoc_pragma->entity.ptr == entity_ptr) break; } /* for */ check_assertion_str((assoc_pragma != NULL) || (prev_assoc_pragma != NULL), "find_assoc_pragma: pragma not found"); return assoc_pragma; } /* find_assoc_pragma */ #if !STANDALONE_UTILITY_PROGRAM void add_to_destructions_list(a_dynamic_init_ptr dip, an_object_lifetime_ptr olp) /* Add the indicated dynamic init entry to the destructions list of the indicated object lifetime entry. */ { check_assertion_str2(in_file_scope(olp) == in_file_scope(dip), "add_to_destructions_list: object lifetime", "and dynamic init in different memory regions"); check_assertion_str2(dip->lifetime == NULL, "add_to_destructions_list:", "entry is already on a destructions list"); /* Add the dynamic init entry to the front of the destructions list for the lifetime. (It's on the front because the last entry constructed will be the first entry destructed.) */ dip->next_in_destruction_list = olp->destructions; olp->destructions = dip; /* Update the lifetime pointer in the dynamic init entry. */ dip->lifetime = olp; } /* add_to_destructions_list */ void add_to_end_of_destructions_list( a_dynamic_init_ptr dip, an_object_lifetime_ptr olp, a_boolean update_parent_destruction_sublist) /* Add the indicated dynamic init entry to the end of the destructions list of the indicated object lifetime entry. When update_parent_destruction_sublist is TRUE (typical case), adjust the parent_destruction_sublist pointers of any child lifetimes to point to the new destruction (if needed). */ { a_dynamic_init_ptr last_dip; an_object_lifetime_ptr colp; check_assertion_str2(in_file_scope(olp) == in_file_scope(dip), "add_to_end_of_destructions_list: object lifetime", "and dynamic init in different memory regions"); check_assertion_str2(dip->lifetime == NULL, "add_to_end_of_destructions_list:", "entry is already on a destructions list"); last_dip = olp->destructions; if (last_dip == NULL) { /* The list is empty, so the entry goes at the front. */ olp->destructions = dip; } else { /* Find the end of the list and insert there. */ for (; last_dip->next_in_destruction_list != NULL; last_dip = last_dip->next_in_destruction_list) {} last_dip->next_in_destruction_list = dip; } /* if */ dip->next_in_destruction_list = NULL; /* Update the lifetime pointer in the dynamic init entry. */ dip->lifetime = olp; if (update_parent_destruction_sublist) { /* Any child lifetime whose parent_destruction_sublist indicates that it is at the beginning of the lifetime should include this destruction. */ for (colp = olp->child_lifetime; colp != NULL; colp = colp->next) { if (colp->parent_destruction_sublist == NULL) { colp->parent_destruction_sublist = dip; } /* if */ } /* for */ } /* if */ } /* add_to_end_of_destructions_list */ void add_to_destructions_list_following(a_dynamic_init_ptr dip, a_dynamic_init_ptr new_dip) /* Add new_dip to the destructions list following dip. "Following" means it's the next entry on the next_in_destruction_list chain; the "next" pointer is not set. */ { an_object_lifetime_ptr olp = dip->lifetime; check_assertion(olp != NULL); new_dip->next_in_destruction_list = dip->next_in_destruction_list; dip->next_in_destruction_list = new_dip; new_dip->lifetime = olp; /* We don't need to update the parent_destruction_sublist pointer of any child lifetimes, because the new entry is after the existing entry, so it isn't the end of the list. */ } /* add_to_destructions_list_following */ void record_end_of_lifetime_destruction(a_dynamic_init_ptr dip, a_boolean static_lifetime, a_boolean block_lifetime) /* If the dynamic init entry pointed to by dip has a destructor associated with it, add the entry to the destructors list for the appropriate object lifetime. If static_lifetime is TRUE, the object in question has static storage duration -- it persists till the end of program execution (i.e., till final object clean up). If block_lifetime is TRUE, use the innermost olk_block or olk_block_after_label object lifetime (i.e., skip the current object lifetime if it is an expr-temporary lifetime). This routine is also called for variable-length arrays (VLAs) in C++, where their deallocation is treated as a form of destruction. C has no object lifetime entries, and marks the point of deallocation of a VLA with an enk_vla_dealloc node instead. */ { an_object_lifetime_ptr olp; db_enter(4, "record_end_of_lifetime_destruction"); if ((dip->destructor != NULL #if VLA_DEALLOCATION_REQUIRED || (is_dynamic_init_for_vla(dip) && !C_mode()) #endif /* VLA_DEALLOCATION_REQUIRED */ ) && /* Do not save destructions in prototype instantiations unless we are saving the prototype instantiations (always save them if we're currently doing IL lowering). */ ( #if DO_IL_LOWERING il_lowering_underway || #endif /* DO_IL_LOWERING */ (depth_scope_stack == NO_SCOPE_DEPTH || !is_template_dependent_context()))) { check_assertion(depth_scope_stack == NO_SCOPE_DEPTH || !is_template_dependent_context()); /* This is a destructible entity. */ if (static_lifetime) { /* Note that we do NOT use depth_innermost_function_scope, as it would not be set correctly during IL lowering. */ if (innermost_function_scope != NULL) { /* The object is a local static variable. Use the list on the scope entry for the function. */ a_scope_ptr sp = innermost_function_scope; olp = sp->variant.routine.lifetime_of_local_static_vars; if (olp == NULL) { olp = alloc_object_lifetime( (an_object_lifetime_kind)olk_function_static); bind_object_lifetime(olp, (an_il_entry_kind)iek_scope, (char *)sp); } /* if */ } else { /* The object is a static object outside a function context -- it belongs to the lifetime of the file scope itself. */ olp = scope_stack[DEPTH_OF_FILE_SCOPE].curr_scope_object_lifetime; } /* if */ } else { an_object_lifetime_ptr temp_olp = NULL; /* Not a static lifetime. */ if (block_lifetime) { /* Want the innermost block lifetime. */ /* Skip an expr-temporary lifetime, if any. */ olp = innermost_block_object_lifetime(curr_object_lifetime); } else { /* The default case is to use whatever is on top of the object lifetime stack. */ olp = curr_object_lifetime; } /* if */ temp_olp = dip->init_expr_lifetime; if (temp_olp == NULL) temp_olp = curr_object_lifetime; if (temp_olp != olp) { /* This entity is initialized during a nested object lifetime. If the nested lifetime has any destructible temporaries, they will be destroyed after this entity has been constructed. Adjust the parent pointer from the nested lifetime so that it includes this entity. */ /* Find the lifetime immediately under the olp lifetime and adjust its parent_destruction_sublist. */ /* Note that some of the entries on the list may relate to freeing of storage allocated by a "new" (is_freeing_of_storage_on_exception is TRUE) rather than being temporaries in the strict sense. */ while (temp_olp->parent_lifetime != olp) { temp_olp = temp_olp->parent_lifetime; } /* while */ if (temp_olp->destructions != NULL) { temp_olp->parent_destruction_sublist = dip; dip->overlaps_temps_in_inner_lifetime = TRUE; dip->lifetime_of_overlapping_temps = temp_olp; } /* if */ } /* if */ } /* if */ /* Now that we've determined the appropriate object lifetime, add the dynamic init entry to its destructions list. */ add_to_destructions_list(dip, olp); #if DEBUG if (debug_level >= 4) { db_pending_destructions(dip, (an_object_lifetime_ptr)NULL); } /* if */ #endif /* DEBUG */ } /* if */ db_exit(); } /* record_end_of_lifetime_destruction */ void record_partial_aggregate_cleanup_destruction(a_dynamic_init_ptr dip, a_boolean evaluated) /* dip is the initialization of a member of an aggregate. If dip indicates a destructor, record a special partial-aggregate exception-cleanup destruction for the dynamic initialization. "evaluated" is TRUE if the expression is evaluated (e.g., it's FALSE for the operand of a sizeof and also FALSE for a dead part of a short-circuiting operation). */ { if (dip->is_creation_of_initializer_list_object) { /* The lifetime of the array underlying an std::initializer_list object matches the lifetime of the object. See core issue 1290. Add an aggregate cleanup for the array also, and add it first so if both have destructors (not currently possible, because std::initializer_list has no destructor) the array will be destroyed last. */ a_dynamic_init_ptr dipa = effective_dynamic_init_for_initializer_list_object(dip, (a_type **)NULL); check_assertion(dipa->lifetime == NULL); record_partial_aggregate_cleanup_destruction(dipa, evaluated); } /* if */ if (dip->destructor != NULL) { dip->destruction_is_for_partially_constructed_aggregate = TRUE; if (evaluated) { /* Note -- static_lifetime is FALSE because (for function-local static variables) even though the underlying entity has static lifetime, the lifetime of the destruction is as though it were automatic. */ record_end_of_lifetime_destruction(dip, /*static_lifetime=*/FALSE, /*block_lifetime=*/FALSE); } /* if */ } /* if */ } /* record_partial_aggregate_cleanup_destruction */ static void move_destruction_to_curr_object_lifetime(a_dynamic_init_ptr dip) /* Move a dynamic init entry representing a destruction (or a list thereof) to the destructions list associated with the current object lifetime. This routine is called to promote the destruction list from an object lifetime that is being discarded. */ { if (dip->next_in_destruction_list != NULL) { /* This dynamic init entry is part of a list. Preserve the original order by processing the next entry first. */ move_destruction_to_curr_object_lifetime(dip->next_in_destruction_list); dip->next_in_destruction_list = NULL; } /* if */ /* Disassociate this destruction from the old lifetime. */ dip->lifetime = NULL; record_end_of_lifetime_destruction(dip, /*static_lifetime=*/FALSE, /*block_lifetime=*/FALSE); } /* move_destruction_to_curr_object_lifetime */ void promote_lifetime_contents_to_curr_object_lifetime(an_object_lifetime *olp) /* Promote the contents of the indicated object lifetime into the current object lifetime. */ { /* We assume the old point of insertion into the parent lifetime is irrelevant. */ olp->parent_destruction_sublist = NULL; /* Promote the destructions, if any. */ if (olp->destructions != NULL) { move_destruction_to_curr_object_lifetime(olp->destructions); olp->destructions = NULL; } /* if */ /* Promote the child lifetimes, if any. */ if (olp->child_lifetime != NULL) { an_object_lifetime_ptr clp = olp->child_lifetime; while (clp->next != NULL) { clp->parent_lifetime = curr_object_lifetime; clp = clp->next; } /* while */ clp->parent_lifetime = curr_object_lifetime; clp->next = curr_object_lifetime->child_lifetime; curr_object_lifetime->child_lifetime = olp->child_lifetime; olp->child_lifetime = NULL; } /* if */ } /* promote_lifetime_contents_to_curr_object_lifetime */ #if DEBUG void db_destruction(a_dynamic_init_ptr dip) /* Dump debug information on a dynamic init entry insofar as it represents a destruction. */ { if (dip->variable != NULL) { fputs("variable: \"", f_debug); db_name_full(&dip->variable->source_corresp, iek_variable); fputs("\", ", f_debug); } /* if */ db_destructor(dip); } /* db_destruction */ void db_object_lifetime_name(an_object_lifetime_ptr olp) /* Dump the "name" of an object lifetime (really, some identifying information about it). */ { a_const_char *str; switch (olp->kind) { case olk_block_after_label: str = "block_after_label"; break; case olk_global_static: str = "global_static"; break; case olk_block: str = "block"; break; case olk_function_static: str = "function_static"; break; case olk_expr_temporary: str = "expr_temporary"; break; case olk_try_block: str = "try_block"; break; default: str = "***BAD LIFETIME KIND***"; break; } /* switch */ fprintf(f_debug, "%s [", str); if (olp->kind == (an_object_lifetime_kind)olk_block_after_label) { if (olp->entity.kind == iek_statement) { a_statement_ptr sp = (a_statement_ptr)olp->entity.ptr; if (sp->kind == (a_statement_kind)stmk_label) { fputc('"', f_debug); db_name(&sp->variant.label.ptr->source_corresp); fputs("\" ", f_debug); } else { db_statement_kind((a_statement_kind)sp->kind); fputs("-stmt", f_debug); } /* if */ } /* if */ fputs("==> ", f_debug); do { olp = olp->parent_lifetime; } while (olp != NULL && olp->kind == (an_object_lifetime_kind)olk_block_after_label); } /* if */ if (olp == NULL) { fputs("", f_debug); } else if (olp->entity.kind == iek_scope) { db_scope((a_scope_ptr)olp->entity.ptr); } else if (olp->entity.kind == iek_expr_node) { fprintf(f_debug, "expr-node@%p:", (a_void_ptr)olp->entity.ptr); db_expr_summary((an_expr_node_ptr)(olp->entity.ptr)); } else if (olp->entity.kind == iek_none) { fputs("", f_debug); } else { fprintf(f_debug, "%s@%p", il_entry_kind_names[(int)olp->entity.kind], (a_void_ptr)olp->entity.ptr); } /* if */ fputc(']', f_debug); } /* db_object_lifetime_name */ void db_object_lifetime(an_object_lifetime_ptr olp) /* Dump debug information about an object lifetime entry. */ { a_dynamic_init_ptr dip; if (olp == NULL) { fputs("null object lifetime\n", f_debug); } else { db_object_lifetime_name(olp); /* Dump the "name" of the parent. */ if (olp->parent_lifetime != NULL) { fprintf(f_debug, "\n parent_lifetime = "); db_object_lifetime_name(olp->parent_lifetime); } /* if */ /* Dump the "name" of each of the children. */ if (olp->child_lifetime != NULL) { an_object_lifetime_ptr temp = olp->child_lifetime->next; fprintf(f_debug, "\n child_lifetime = "); db_object_lifetime_name(olp->child_lifetime); for (; temp != NULL; temp = temp->next) { fputs("\n ", f_debug); db_object_lifetime_name(temp); } /* for */ } /* if */ /* Dump the "name" of the next entry in the sibling list. */ if (olp->next != NULL) { fprintf(f_debug, "\n next = "); db_object_lifetime_name(olp->next); } /* if */ /* Dump the destructions list. */ dip = olp->destructions; if (dip != NULL) { fprintf(f_debug, "\n destructions = "); db_destruction(dip); dip = dip->next_in_destruction_list; for (; dip != NULL; dip = dip->next_in_destruction_list) { fputs("\n ", f_debug); db_destruction(dip); } /* for */ } /* if */ fputc('\n', f_debug); } /* if */ } /* db_object_lifetime */ void db_object_lifetime_stack(void) /* Dump information about the object lifetime stack (i.e., start with curr_object_lifetime and dump the name of each entry found by following the parent_lifetime pointer. */ { an_object_lifetime_ptr olp = curr_object_lifetime; fprintf(f_debug, "object_lifetime_stack:%s\n", olp == NULL ? " " : ""); for (; olp != NULL; olp = olp->parent_lifetime) { fputs(" ", f_debug); db_object_lifetime_name(olp); fputc('\n', f_debug); } /* for */ } /* db_object_lifetime_stack */ void db_pending_destructions(a_dynamic_init_ptr dip, an_object_lifetime_ptr stop_at) /* Dump all the destructions that are active on the object lifetime stack, stopping when the object lifetime indicated by stop_at is reached. */ { an_object_lifetime_ptr olp; if (dip != NULL && dip->lifetime != stop_at) { olp = dip->lifetime; fputs("pending destructions:\n", f_debug); for (; olp != NULL && olp != stop_at; olp = olp->parent_lifetime) { fputs(" --for lifetime associated with ", f_debug); /* Dump the "name" of each object lifetime. */ db_object_lifetime_name(olp); fputc(':', f_debug); /* Dump the destructions associated with it. */ if (dip == NULL) { fputs(" ", f_debug); } else { for (; dip != NULL; dip = dip->next_in_destruction_list) { fputs("\n ", f_debug); db_destruction(dip); } /* for */ } /* if */ fputc('\n', f_debug); /* Note: on a parent destruction list, only those following parent_destruction_sublist are active from the point of view of the current stack. Set dip to start at the right entry. */ dip = olp->parent_destruction_sublist; } /* for */ } /* if */ } /* db_pending_destructions */ void db_object_lifetime_with_indentation(an_object_lifetime_ptr olp, a_const_char *str) /* Display an object lifetime in a special format, for use when dump_lifetimes has been enabled at the command line. The display line includes the current sequence number, indentation corresponding to the depth of object lifetime stack, a string supplied by the caller, and the object lifetime "name". */ { an_object_lifetime_ptr parent = olp->parent_lifetime; fprintf(f_debug, "OL (%p)-%.4d..", (void*)olp, (int)pos_curr_token.seq); if (lifetime_is(olp, olk_block_after_label)) { while (lifetime_is(parent, olk_block_after_label)) { parent = parent->parent_lifetime; } /* while */ parent = parent->parent_lifetime; } /* if */ for (; parent != NULL; parent = parent->parent_lifetime) { if (lifetime_is(parent, olk_block_after_label)) { fputs("..", f_debug); } /* if */ } /* for */ if (str != NULL) fputs(str, f_debug); db_object_lifetime_name(olp); fputc('\n', f_debug); } /* db_object_lifetime_with_indentation */ void db_object_lifetime_tree(an_object_lifetime_ptr olp) /* Dump information about the indicated object lifetime and the lifetimes and destructions under it. */ { db_object_lifetime(olp); if (olp != NULL) { an_object_lifetime_ptr temp = olp->child_lifetime; for (; temp != NULL; temp = temp->next) { db_object_lifetime_tree(temp); } /* for */ } /* if */ } /* db_object_lifetime_tree */ #endif /* DEBUG */ void free_object_lifetime(an_object_lifetime_ptr olp) /* Return an object lifetime to the appropriate available list. */ { an_object_lifetime_ptr *avail_list_ptr; a_scope_depth scope_depth; db_enter(5, "free_object_lifetime"); if (in_file_scope(olp)) { /* Return the entry to the file scope's available list. */ scope_depth = DEPTH_OF_FILE_SCOPE; } else { /* Use the current function scope. */ scope_depth = depth_innermost_function_scope; } /* if */ /* When IL lowering generates routines, there is no scope stack entry, and therefore no available list can be maintained. */ if (scope_depth != NO_SCOPE_DEPTH) { /* Copy the address of the available list. */ avail_list_ptr = &scope_stack[scope_depth].object_lifetime_avail_list; /* Link it onto the front of the available list. */ olp->next = *avail_list_ptr; *avail_list_ptr = olp; } /* if */ db_exit(); } /* free_object_lifetime */ static an_object_lifetime_ptr *addr_of_lifetime_ptr( an_il_entry_kind entity_kind, char *entity_ptr, an_object_lifetime_kind kind) /* Given an IL entry kind and a pointer to the entry, return the address of the field of that entry that points to an object lifetime. Since scope entries have two such pointers, the object lifetime kind is also passed in to help determine which address to return. */ { an_object_lifetime_ptr *lifetime_addr = NULL; switch (entity_kind) { case iek_scope: /* There are three lifetime pointers in a scope entry. Use kind to select the right one. */ if (kind == (an_object_lifetime_kind)olk_function_static) { lifetime_addr = &((a_scope_ptr)entity_ptr)-> variant.routine.lifetime_of_local_static_vars; } else { lifetime_addr = &((a_scope_ptr)entity_ptr)->lifetime; } /* if */ break; case iek_expr_node: check_assertion(((an_expr_node_ptr)entity_ptr)->kind == (an_expr_node_kind)enk_object_lifetime); lifetime_addr = &((an_expr_node_ptr)entity_ptr)-> variant.object_lifetime.ptr; break; case iek_block: lifetime_addr = &((a_block_ptr)entity_ptr)->lifetime; break; case iek_try_supplement: lifetime_addr = &((a_try_supplement_ptr)entity_ptr)->lifetime; break; case iek_dynamic_init: lifetime_addr = &((a_dynamic_init_ptr)entity_ptr)->init_expr_lifetime; break; case iek_local_static_variable_init: lifetime_addr = &((a_local_static_variable_init_ptr)entity_ptr)->lifetime; break; default: unexpected_condition_str("addr_of_lifetime_ptr: bad il entry kind"); } /* switch */ return lifetime_addr; } /* addr_of_lifetime_ptr */ void bind_object_lifetime(an_object_lifetime_ptr olp, an_il_entry_kind entity_kind, char *entity_ptr) /* Set the object lifetime entry pointed to by olp to point to the IL entry represented by entity_kind and entity_ptr, and set the IL entry to point back to it. This function should not be called for olk_block_after_label lifetimes, since those are never bound. */ { an_object_lifetime_ptr *lifetime_addr; #if CHECKING a_const_char *str = "bind_object_lifetime:"; check_assertion_str2(entity_ptr != NULL, str, "NULL entity"); check_assertion_str2(olp->entity.ptr == NULL, str, "lifetime already bound"); /* Be sure the object lifetime kind is consistent with the kind of entity with which the object lifetime is being bound. */ switch (olp->kind) { case olk_function_static: check_assertion_str2((entity_kind == (an_il_entry_kind)iek_scope) && (((a_scope_ptr)entity_ptr)->kind == (a_scope_kind)sck_function), str, "bad entity or scope kind for olk_function_static"); break; case olk_global_static: check_assertion_str2((entity_kind == (an_il_entry_kind)iek_scope) && (((a_scope_ptr)entity_ptr)->kind == (a_scope_kind)sck_file), str, "bad entity or scope kind for olk_global_static"); break; case olk_block: switch (entity_kind) { case iek_scope: check_assertion_str2(is_local_scope_kind( ((a_scope_ptr)entity_ptr)->kind), str, "bad scope kind for olk_block"); break; case iek_local_static_variable_init: case iek_block: /* Okay. */ break; default: unexpected_condition_str2(str, "bad entity kind for olk_block"); } /* switch */ break; case olk_block_after_label: switch (entity_kind) { case iek_statement: /* Okay. */ break; default: unexpected_condition_str2( str, "bad entity kind for olk_block_after_label"); } /* switch */ break; case olk_expr_temporary: switch (entity_kind) { case iek_block: case iek_expr_node: case iek_dynamic_init: /* Okay. */ break; default: unexpected_condition_str2(str, "bad entity kind for olk_expr_temporary"); } /* switch */ break; case olk_try_block: switch (entity_kind) { case iek_try_supplement: case iek_block: /* Okay. */ break; default: unexpected_condition_str2(str, "bad entity kind for olk_try_block"); } /* switch */ break; default: unexpected_condition_str2(str, "bad object lifetime kind"); } /* if */ #endif /* CHECKING */ /* Point the object lifetime at the IL entry. */ olp->entity.kind = entity_kind; olp->entity.ptr = entity_ptr; if (olp->kind != (an_object_lifetime_kind)olk_block_after_label) { /* Get the address of the appropriate field of the IL entry and point back to the object lifetime entry. */ lifetime_addr = addr_of_lifetime_ptr(entity_kind, entity_ptr, olp->kind); check_assertion(*lifetime_addr == NULL); *lifetime_addr = olp; } /* if */ #if DEBUG if (db_flag_is_set("dump_lifetimes")) { db_object_lifetime_with_indentation(olp, "Binding: "); } /* if */ #endif /* DEBUG */ } /* bind_object_lifetime */ void unbind_object_lifetime(an_object_lifetime_ptr olp) /* Undo the binding between an object lifetime entry and the IL entry to which it points. */ { an_object_lifetime_ptr *lifetime_addr; if (olp->kind != (an_object_lifetime_kind)olk_block_after_label) { /* Get the address of the appropriate field of the IL entry so that the lifetime pointer can be cleared. */ lifetime_addr = addr_of_lifetime_ptr((an_il_entry_kind)olp->entity.kind, olp->entity.ptr, olp->kind); *lifetime_addr = NULL; } /* if */ /* Clear the fields in the object lifetime, too. */ olp->entity.kind = iek_none; olp->entity.ptr = NULL; } /* unbind_object_lifetime */ void push_or_repush_object_lifetime(an_il_entry_kind entity_kind, char *entity_ptr, an_object_lifetime_ptr olp, an_object_lifetime_kind kind, a_boolean is_reactivation) /* Push a new object lifetime entry (of the given kind) on the object lifetime stack, either the given entry (olp) or a newly allocated one. is_reactivation is TRUE if the call is for the reactivation of a local scope. If entity_ptr is non-NULL, bind the object lifetime to the IL entity with which it is associated unless olp is non-NULL and is_reactivation is TRUE (in that case the binding was done when the reactivated scope was first created). (When entity_ptr is NULL, the binding takes place later, when we are sure the entry is needed.) */ { an_object_lifetime_ptr parent; a_boolean no_bind; db_enter(3, "push_or_repush_object_lifetime"); check_assertion_str(kind != (an_object_lifetime_kind)olk_function_static, "push_or_repush_object_lifetime: olk_function_static not allowed"); if (olp == NULL) { olp = alloc_object_lifetime(kind); no_bind = FALSE; } else { kind = olp->kind; no_bind = is_reactivation; } /* if */ if (kind == (an_object_lifetime_kind)olk_global_static) { /* No parent pointer. */ } else if (is_reactivation && olp->parent_lifetime != NULL) { /* A reactivation and this entry is already recorded in the overall tree. */ } else { /* Ordinarily, an object lifetime cannot be the child of a lifetime for a temporary. The one exception is a lifetime embedded in a GNU statement expression. */ check_assertion_str2(curr_object_lifetime->kind != olk_expr_temporary || (gpp_mode && entity_kind == iek_scope), "push_or_repush_object_lifetime:", "pushing on top of olk_expr_temporary not allowed"); /* Link the new entry into the object lifetime tree. */ parent = curr_object_lifetime; if (entity_kind == (an_il_entry_kind)iek_scope && kind == (an_object_lifetime_kind)olk_block && in_file_scope(olp) && !in_file_scope(parent)) { /* This can happen with a GNU statement expression appearing, e.g., in a local enumerator constant. Use the global scope static lifetime as the parent instead. */ parent = scope_stack[DEPTH_OF_FILE_SCOPE].curr_scope_object_lifetime; } /* if */ olp->parent_lifetime = parent; if (entity_kind == (an_il_entry_kind)iek_scope && entity_ptr != NULL && ((a_scope_ptr)entity_ptr)->kind == (a_scope_kind)sck_function) { /* This is an object lifetime for a function scope; its parent pointer is the file scope lifetime entry, but it's an "implicit" child of the latter -- because of a memory region incompatibility, olp doesn't appear explicitly on the child_lifetime list of its parent . */ check_assertion(scope_stack[DEPTH_OF_FILE_SCOPE].il_scope == (a_scope_ptr)parent->entity.ptr); /* Don't add the current entry to the parent's list of children, and don't update the sibling pointer. */ parent->has_implicit_child = TRUE; } else if (in_file_scope(olp) == in_file_scope(parent)) { /* If the parent already has a list of children, add the new entry to the front of the list. */ olp->next = parent->child_lifetime; parent->child_lifetime = olp; if (kind == (an_object_lifetime_kind)olk_block_after_label) { parent->has_block_after_label_child_lifetime = TRUE; } /* if */ /* Record the current position in the dynamic inits list of the parent. */ olp->parent_destruction_sublist = parent->destructions; #if CHECKING } else { if (in_file_scope(parent)) { unexpected_condition_str2( "push_or_repush_object_lifetime: parent is in", "file scope memory, new olp is not"); } else { unexpected_condition_str2( "push_or_repush_object_lifetime: new olp is in", "file scope memory, parent is not"); } /* if */ #endif /* CHECKING */ } /* if */ } /* if */ if (entity_ptr != NULL) { if (!no_bind) { /* Bind the object lifetime and the entity with which it is associated. */ bind_object_lifetime(olp, entity_kind, entity_ptr); } else { check_assertion(entity_kind == (an_il_entry_kind)iek_scope); } /* if */ } /* if */ #if DEBUG if (db_flag_is_set("dump_lifetimes")) { db_object_lifetime_with_indentation(olp, "Push or repush: "); } /* if */ #endif /* DEBUG */ /* Now set the new entry to be the current object lifetime. */ curr_object_lifetime = olp; #if DEBUG if (debug_level >= 3) db_object_lifetime_stack(); #endif /* DEBUG */ db_exit(); } /* push_or_repush_object_lifetime */ static a_boolean any_destruction_has_temp_lifetime(an_object_lifetime_ptr olp) /* Do through the destructions for lifetime *olp and return TRUE if any is marked as having "temporary lifetime" -- i.e., a lifetime (long or short) that is not governed by the lifetime of another entity to which it is bound (e.g., to a variable of reference type). */ { a_boolean found = FALSE; a_dynamic_init_ptr dip = olp->destructions; for (; dip != NULL; dip = dip->next_in_destruction_list) { if (dip->has_temporary_lifetime) { found = TRUE; break; } /* if */ } /* for */ return found; } /* any_destruction_has_temp_lifetime */ static a_boolean has_child_with_temporary_lifetime(an_object_lifetime_ptr olp) /* Go through the children of olp and return TRUE if any has a destruction marked as having "temporary lifetime". */ { a_boolean found = FALSE; an_object_lifetime_ptr child = olp->child_lifetime; for (; child != NULL; child = child->next) { if (any_destruction_has_temp_lifetime(child)) { found = TRUE; break; } /* if */ } /* for */ return found; } /* has_child_with_temporary_lifetime */ a_boolean is_useless_object_lifetime(an_object_lifetime_ptr olp) /* Return TRUE if the object lifetime entry pointed to by olp is "useless" -- that is, there is no justification for its remaining in the IL. For the most part, it is useless if it has no dynamic initializations associated with it. Entries associated with scopes must also have no child entries. */ { a_boolean is_useless = FALSE; a_boolean do_child_check; if (olp->destructions != NULL) { /* is_useless = FALSE. */ } else { switch (olp->kind) { case olk_global_static: /* The file scope object lifetime is preserved if it has any "implicit children" -- i.e., any function scope object lifetimes that are not useless; the latter point to the file scope as parent_lifetime. Explicit children also make the lifetime useful. */ if (olp->child_lifetime == NULL && !olp->has_implicit_child) is_useless = TRUE; break; case olk_block: do_child_check = TRUE; if (olp->entity.kind == iek_scope) { a_scope_ptr sp = (a_scope_ptr)olp->entity.ptr; if (sp->kind == (a_scope_kind)sck_block && sp->variant.assoc_handler != NULL) { /* The lifetime associated with a catch clause is retained in the IL even if it has no destructions and no children. */ do_child_check = FALSE; #if DO_IL_LOWERING #if NEW_CAN_BE_FOLDED_INTO_CTOR } else if (exceptions_enabled && sp->kind == (a_scope_kind)sck_function && sp->variant.routine.ptr->special_kind == (a_special_function_kind)sfk_constructor && class_type_supp(parent_class_of(sp->variant.routine.ptr)) ->assoc_operator_new_routine != NULL) { /* When exceptions are enabled, a constructor with the allocation folded in needs an object lifetime so an entry for the deletion of the storage can be added. */ do_child_check = FALSE; #endif /* NEW_CAN_BE_FOLDED_INTO_CTOR */ #endif /* DO_IL_LOWERING */ } else if (olp->parent_lifetime != NULL && olp->parent_lifetime->kind == (an_object_lifetime_kind)olk_expr_temporary && olp->block_lifetime_with_label_or_goto) { /* A block lifetime for a GNU statement expression that contains a label or goto statement: Keep it to avoid having to promote a non-block lifetime to be a child of an olk_expr_temporary lifetime. */ do_child_check = FALSE; } else if (olp->child_lifetime == NULL) { /* No children, no destructions. */ is_useless = TRUE; do_child_check = FALSE; } else if (sp->kind == (a_scope_kind)sck_function) { /* A function lifetime with children is retained in the IL even if it has no destructions. */ do_child_check = FALSE; } /* if */ } else if (exceptions_enabled && olp->entity.kind == iek_local_static_variable_init) { /* A lifetime that surrounds the initialization of a local static variable is kept when exceptions are enabled, because it delimits the region within which the initialization must be undone and set up to restart if an exception is thrown during the initialization. */ do_child_check = FALSE; } else if (olp->entity.kind == iek_block && olp->child_lifetime != NULL && depth_innermost_function_scope == depth_scope_stack && innermost_function_scope->variant.routine.ptr-> is_coroutine) { /* This is the block lifetime for a coroutine. Since the coroutine body is moved around, the lifetime is needed to preserve destruction order. */ do_child_check = FALSE; } /* if */ if (do_child_check) { /* A block lifetime bound to a scope but not covered by the preceding tests, or else a block lifetime bound to a block statement (the cfront dependent statement case), or else an unbound object lifetime. */ if (olp->has_block_after_label_child_lifetime) { /* A lifetime is kept in the IL even if it has no destructions of its own if it has a block-after-label child lifetime. */ } else if (has_child_with_temporary_lifetime(olp)) { /* A block lifetime is kept in the IL if it has a child lifetime that represents an expression temporary. This is to keep such a temporary from "floating up" the lifetime tree because its parent was deemed useless. */ } else { is_useless = TRUE; } /* if */ } /* if */ break; case olk_block_after_label: if (has_child_with_temporary_lifetime(olp)) { /* Do not remove a block-after-label lifetime if it has any children that have temporary lifetimes. */ } else if (long_lifetime_temps && any_destruction_has_temp_lifetime(olp->parent_lifetime)) { /* If temps have long lifetimes, the label is the point at which those temps have to be destroyed. Preserve the current lifetime in that case, too. */ } else { is_useless = TRUE; } /* if */ break; case olk_expr_temporary: is_useless = TRUE; break; case olk_try_block: /* The lifetime associated with a try block is retained in the IL even if it has no destructions and no children. */ break; case olk_function_static: /* Should not have been created unless there were destructions. */ default: unexpected_condition_str2("is_useless_object_lifetime:", "bad object lifetime kind"); } /* switch */ } /* if */ return is_useless; } /* is_useless_object_lifetime */ void remove_from_destruction_list(a_dynamic_init_ptr dip) /* If the specified dynamic init entry is associated with an object lifetime, unlink it from the latter's destructions list, and clear the pointer in the dynamic init entry to the object lifetime. Free a destructible entity description if one is associated with this dip. */ { an_object_lifetime_ptr olp = dip->lifetime, colp; a_dynamic_init_ptr prev; if (olp != NULL) { /* There is an associated object lifetime. */ if (olp->destructions == dip) { /* dip is the head of the destructions list. */ olp->destructions = dip->next_in_destruction_list; if (olp->destructions == NULL) { /* The destructions list is now empty. If there is an initialization in an enclosing lifetime whose initialization overlapped the lifetime of the inner lifetime temporaries, it no longer does. Clear the flag that indicates that. */ a_dynamic_init_ptr outer_dip = olp->parent_destruction_sublist; if (outer_dip != NULL) { if (outer_dip->overlaps_temps_in_inner_lifetime && outer_dip->lifetime_of_overlapping_temps == olp) { outer_dip->overlaps_temps_in_inner_lifetime = FALSE; outer_dip->lifetime_of_overlapping_temps = NULL; olp->parent_destruction_sublist = outer_dip->next_in_destruction_list; } /* if */ } /* if */ } /* if */ } else { /* It's not the head; link around it once the previous entry in the list has been located. */ prev = olp->destructions; for (;;) { check_assertion_str2(prev != NULL, "remove_from_destruction_list:", "dynamic init not on list of assoc lifetime"); if (prev->next_in_destruction_list == dip) { prev->next_in_destruction_list = dip->next_in_destruction_list; break; } /* if */ prev = prev->next_in_destruction_list; } /* for */ } /* if */ /* If any child lifetime has its parent_destruction_sublist pointing to the removed dynamic init, change the pointer to the next entry on the destruction list. */ for (colp = olp->child_lifetime; colp != NULL; colp = colp->next) { if (colp->parent_destruction_sublist == dip) { colp->parent_destruction_sublist = dip->next_in_destruction_list; } /* if */ } /* for */ dip->next_in_destruction_list = NULL; /* Clear the lifetime pointer in the dynamic init entry. Note: it should be set if and only if it is on the list of the entry pointed to. */ dip->lifetime = NULL; } /* if */ #if DO_IL_LOWERING if (dip->destructible_entity_descr != NULL) { free_destructible_entity_descr(dip->destructible_entity_descr); dip->destructible_entity_descr = NULL; } /* if */ #endif /* DO_IL_LOWERING */ } /* remove_from_destruction_list */ static void unlink_object_lifetime(an_object_lifetime_ptr lifetime) /* Unlink the indicated object lifetime from the IL tree. Its child lifetimes, if any, are left attached to it, but will no longer be in the tree because their parent no longer is. */ { an_object_lifetime_ptr parent = lifetime->parent_lifetime; /* This routine doesn't handle implicit children, and the global scope object lifetime cannot be removed. */ check_assertion(parent != NULL); /* Unbind the lifetime unless it's already unbound. */ if (lifetime->entity.kind != iek_none) { unbind_object_lifetime(lifetime); } /* if */ /* Remove the lifetime from its parent's child list. */ if (parent->child_lifetime == lifetime) { parent->child_lifetime = lifetime->next; } else { an_object_lifetime_ptr prev = parent->child_lifetime; while (prev->next != lifetime) { prev = prev->next; check_assertion(prev != NULL); } /* while */ prev->next = lifetime->next; } /* if */ } /* unlink_object_lifetime */ static void process_dynamic_init_for_unlink_destructions( a_dynamic_init_ptr dip, ARG_UNUSED an_expr_or_stmt_traversal_block_ptr tblock) /* Called by the expression traversal routines to handle unlinking for unlink_expr_destructions. */ { if (dip->init_expr_lifetime != NULL) { unlink_object_lifetime(dip->init_expr_lifetime); } /* if */ if (dip->lifetime != NULL) { remove_from_destruction_list(dip); } /* if */ } /* process_dynamic_init_for_unlink_destructions */ static void process_expr_for_unlink_destructions( an_expr_node_ptr expr, ARG_UNUSED an_expr_or_stmt_traversal_block_ptr tblock) /* Called by the expression traversal routines to handle unlinking for unlink_expr_destructions. */ { if (expr->kind == (an_expr_node_kind)enk_object_lifetime) { unlink_object_lifetime(expr->variant.object_lifetime.ptr); } else if (expr->kind == (an_expr_node_kind)enk_statement) { /* Save time by not visiting the subtree for a statement expression, because its destructions are self-contained. */ tblock->suppress_subtree_walk = TRUE; } /* if */ } /* process_expr_for_unlink_destructions */ void unlink_expr_destructions(an_expr_node_ptr expr) /* Unlink any destructions in the tree of the indicated expression from their object lifetimes. This is usually done because the expression is being discarded. */ { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = process_expr_for_unlink_destructions; tblock.process_dynamic_init = process_dynamic_init_for_unlink_destructions; traverse_expr(expr, &tblock); } /* unlink_expr_destructions */ void mark_object_lifetime_as_useless(an_object_lifetime_ptr olp) /* Mark the indicated object lifetime entry as being useless, i.e., as one that can be discarded when popped off the object lifetime stack. This is needed when errors occur, to avoid complaints at the time of popping that an object lifetime containing destructions has not been bound to anything. The object lifetime is marked as useless by removing all the destructions on its list and those of its children. Since that's destructive, it should only be done when there were errors. */ { /* Clear the destructions pointer by removing the entries and resetting their own pointers properly. */ while (olp->destructions != NULL) { check_assertion(olp->destructions->lifetime == olp); remove_from_destruction_list(olp->destructions); } /* while */ { an_object_lifetime_ptr child_olp; /* Get rid of any children. */ for (child_olp = olp->child_lifetime; child_olp != NULL; child_olp = child_olp->next) { mark_object_lifetime_as_useless(child_olp); } /* for */ olp->child_lifetime = NULL; } /* Check that the object lifetime is indeed useless. (However, the block lifetime associated with a GNU statement expression is not always treated as useless because we want to avoid promoting a non-block lifetime to become the child of an olk_expr_temporary lifetime.) */ check_assertion_str(is_useless_object_lifetime(olp) || olp->parent_lifetime->kind == (an_object_lifetime_kind)olk_expr_temporary, "failed to mark object lifetime as useless"); } /* mark_object_lifetime_as_useless */ a_boolean pop_object_lifetime_full(ARG_UNUSED a_boolean unbound_okay) /* Pop an object lifetime off the object lifetimes stack. Check whether it needs to be kept in the IL tree. If not, unlink it from the IL and return it to the appropriate available list. Return TRUE if the object lifetime is retained in the IL tree. If unbound_okay is TRUE, it's okay if a lifetime being left in the IL is not bound to an IL entry. */ { a_boolean is_implicit_child = FALSE; an_object_lifetime_ptr olp, parent; a_boolean is_retained_in_il; db_enter(3, "pop_object_lifetime_full"); #if DEBUG if (debug_level >= 3) { fputs("curr_object_lifetime = ", f_debug); db_object_lifetime(curr_object_lifetime); } /* if */ #endif /* DEBUG */ olp = curr_object_lifetime; /* Pop the lifetime entry -- that is, update curr_object_lifetime to point to its parent entry. */ curr_object_lifetime = olp->parent_lifetime; /* Do additional processing connected with whether the entry remains in the IL or should be removed. */ if (olp->kind == (an_object_lifetime_kind)olk_block && olp->entity.kind == iek_scope && ((a_scope_ptr)olp->entity.ptr)->kind == (a_scope_kind)sck_function) { /* This is an object lifetime for a function scope; its parent pointer is the file scope lifetime entry, but it's an "implicit" child of the latter -- because of a memory region incompatibility, olp doesn't appear explicitly on the child_lifetime list of its parent . */ is_implicit_child = TRUE; } /* if */ /* Determine whether the entry needs to be kept in the IL at all. If not, modify all related pointers and then return it to an available list. */ if (is_useless_object_lifetime(olp)) { #if DEBUG if (db_flag_is_set("dump_lifetimes")) { if (olp->kind != (an_object_lifetime_kind)olk_expr_temporary || long_lifetime_temps) { db_object_lifetime_with_indentation(olp, "Discarding: "); } /* if */ } /* if */ #endif /* DEBUG */ /* Unlink the object lifetime entry from its parent, children, and siblings. */ parent = olp->parent_lifetime; /* Unless *olp is an "implicit child", the lifetime entry that's no longer needed should be the first entry on the parent's child list. */ if (parent == NULL || is_implicit_child) { /* We must be disposing of the object lifetime entry of a file or function scope, so there must not be any children. */ check_assertion(olp->child_lifetime == NULL); } else { /* Remove the current object lifetime from the parent's child-lifetime list. Promote its own children, if appropriate. */ an_object_lifetime_ptr child, end_of_child_list, *olp_loc; /* Determine the position of the current object lifetime in its parent's object-lifetime list. */ olp_loc = &parent->child_lifetime; if (parent->child_lifetime != olp) { /* It's not the first in the list, so find the point at which to link around it and at which to insert its children, if required. */ an_object_lifetime_ptr prev = parent->child_lifetime; while (prev->next != olp) { prev = prev->next; check_assertion(prev != NULL); } /* while */ olp_loc = &prev->next; } /* if */ /* Loop through all the children of olp and move them up to the parent's child list -- i.e., promote the children to siblings. */ end_of_child_list = NULL; for (child = olp->child_lifetime; child != NULL; child = child->next) { child->parent_lifetime = parent; child->parent_destruction_sublist = olp->parent_destruction_sublist; end_of_child_list = child; } /* for */ /* If there is a child list, promote it to parent. */ if (olp->child_lifetime != NULL) { end_of_child_list->next = olp->next; *olp_loc = olp->child_lifetime; } else { *olp_loc = olp->next; } /* if */ if (olp->kind == (an_object_lifetime_kind)olk_block_after_label) { /* We have removed a block-after-label child from the parent's list of children. Reset the flag, unless another block-after-label has been promoted in its place. */ if (!olp->has_block_after_label_child_lifetime) { parent->has_block_after_label_child_lifetime = FALSE; } /* if */ } /* if */ } /* if */ /* *olp's former parent and children, if any, should no longer have pointers back to it. Now (to be safe) remove its own pointers. */ olp->parent_lifetime = NULL; olp->child_lifetime = NULL; olp->next = NULL; if (olp->entity.ptr == NULL) { /* It's not been bound to any IL entry. */ } else { /* Unbind from the IL entry with which it is associated. */ unbind_object_lifetime(olp); } /* if */ is_retained_in_il = FALSE; /* Return the entry to its available list. */ free_object_lifetime(olp); } else { /* Be sure an object lifetime that is being left in the IL has been bound to some other IL entity. */ check_assertion_str(unbound_okay || olp->entity.ptr != NULL || olp->kind == (an_object_lifetime_kind)olk_block_after_label, "pop_object_lifetime_full: useful lifetime is unbound"); is_retained_in_il = TRUE; if (is_implicit_child) { /* This is an object lifetime for a function scope that will remain in the IL. Set the global variable to assure that the file scope lifetime entry will be preserved. */ olp->parent_lifetime->has_implicit_child = TRUE; } /* if */ #if DEBUG if (db_flag_is_set("dump_lifetimes")) { db_object_lifetime_with_indentation(olp, "Keeping: "); } /* if */ #endif /* DEBUG */ } /* if */ #if DEBUG if (debug_level >= 3 || db_flag_is_set("dump_lifetimes")) { db_object_lifetime_stack(); } /* if */ #endif /* DEBUG */ db_exit() return is_retained_in_il; } /* pop_object_lifetime_full */ an_object_lifetime_ptr innermost_block_object_lifetime( an_object_lifetime_ptr olp) /* Starting with the object lifetime entry pointed to by olp, advance though its parents and return the first entry that has a kind of olk_block or olk_block_after_label. */ { while (olp->kind != (an_object_lifetime_kind)olk_block && olp->kind != (an_object_lifetime_kind)olk_block_after_label) { olp = olp->parent_lifetime; check_assertion_str(olp != NULL, "innermost_block_object_lifetime: not found"); } /* while */ return olp; } /* innermost_block_object_lifetime */ #if DEBUG void db_scope(a_scope_ptr sp) /* Write out a scope entry for debugging purposes. */ { if (sp == NULL) { (void)fputs("", f_debug); } else { (void)db_scope_kind(sp->kind); (void)fprintf(f_debug, " scope %d", (int)sp->number); if (sp->kind == (a_scope_kind)sck_class_struct_union || sp->kind == (a_scope_kind)sck_enum || sp->kind == (a_scope_kind)sck_function || sp->kind == (a_scope_kind)sck_namespace) { (void)fputs(" (", f_debug); if (sp->kind == (a_scope_kind)sck_class_struct_union || sp->kind == (a_scope_kind)sck_enum) { if (sp->variant.assoc_type != NULL) { db_type_name(sp->variant.assoc_type); } else { fprintf(f_debug, ""); } /* if */ } else if (sp->kind == (a_scope_kind)sck_namespace) { db_name(&sp->variant.assoc_namespace->source_corresp); } else { db_name_full(&sp->variant.routine.ptr->source_corresp, iek_routine); } /* if */ (void)fputc(')', f_debug); } /* if */ } /* if */ } /* db_scope */ static void db_type_list(a_type_ptr type_list, size_t indent, a_boolean do_subscopes) /* Write out a type list, for debugging purposes. indent is the indentation level. If do_subscopes is TRUE, write out the type lists for subscopes as well. */ { a_type_ptr type; a_boolean secondary = FALSE; if (type_list != NULL) secondary = in_secondary_trans_unit(type_list); for (type = type_list; type != NULL; type = type->next) { /* Check for a list that switches between translation units. */ if (secondary != in_secondary_trans_unit(type)) { db_indent(indent); (void)fprintf(f_debug, "***switch between translation units***\n"); secondary = !secondary; } /* if */ db_indent(indent); db_abbreviated_type(type); (void)fprintf(f_debug, "\n"); if (do_subscopes && is_immediate_class_type(type)) { a_class_type_supplement_ptr ctsp = type->variant.class_struct_union.extra_info; if (ctsp != NULL && !scope_is_null_or_placeholder(ctsp->assoc_scope)) { db_type_list(ctsp->assoc_scope->types, indent+2, do_subscopes); } /* if */ } /* if */ } /* for */ } /* db_type_list */ void db_scope_type_list(a_scope_ptr scope, size_t indent, a_boolean do_subscopes) /* Write out the type list for a scope, for debugging purposes. indent is the indentation level. If do_subscopes is TRUE, write out the type lists for subscopes as well. */ { db_indent(indent); (void)fprintf(f_debug, "Type list for "); db_scope(scope); (void)fprintf(f_debug, ":\n"); db_type_list(scope->types, indent+2, do_subscopes); } /* db_scope_type_list */ void db_type_lists(a_scope_ptr scope, size_t indent) /* Dump the type lists for the indicated scope and its subscopes, for debug purposes. indent indicates the indentation level. */ { a_namespace_ptr nsp; a_scope_ptr bscope; db_scope_type_list(scope, indent, /*do_subscopes=*/TRUE); for (nsp = scope->namespaces; nsp != NULL; nsp = nsp->next) { if (!nsp->is_namespace_alias) { db_type_lists(nsp->variant.assoc_scope, indent+2); } /* if */ } /* for */ for (bscope = scope->scopes; bscope != NULL; bscope = bscope->next) { db_type_lists(bscope, indent+2); } /* for */ } /* db_type_lists */ #endif /* DEBUG */ #endif /* !STANDALONE_UTILITY_PROGRAM */ #if !STANDALONE_UTILITY_PROGRAM a_boolean has_nonreal_parent_type(a_source_correspondence *scp) /* Return TRUE if the entity specified by scp has a parent type that is a template parameter type or nonreal class. */ { a_boolean result = FALSE; if (scp->is_class_member) { a_type_ptr parent_type = scp_parent_class(scp); if (is_immediate_class_type(parent_type)) { result = parent_type->variant.class_struct_union.is_nonreal_class; } else if (parent_type->kind == (a_type_kind)tk_template_param) { result = TRUE; } /* if */ } /* if */ return result; } /* has_nonreal_parent_type */ void add_to_templates_list(a_template_ptr tp, a_scope_depth scope_depth) /* Add the IL template entry pointed to by tp to the indicated scope. */ { a_scope_stack_entry_ptr ssep; a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; assert_is_valid_scope_depth(scope_depth); ssep = &scope_stack[scope_depth]; if (!prototype_instantiations_in_il && (ssep->in_prototype_instantiation || has_nonreal_parent_type(&tp->source_corresp))) { /* We are not going to record the indicated scope (because it belongs to a prototype instantiation), but the given template must go on a list somewhere because assoc_template fields may point to it. Add it to the file scope. Later, during the file scope wrapup, the file scope templates list will be scanned, and the parent pointer of templates from prototype instantiations will be cleared. */ ssep = &scope_stack[DEPTH_OF_FILE_SCOPE]; } /* if */ sp = ensure_il_scope_exists(ssep); check_assertion_str(sp != NULL, "add_to_templates_list: NULL IL scope"); pointers_block = assoc_pointers_block_of(ssep); if (sp->templates == NULL) { sp->templates = tp; } else { pointers_block->last_template->next = tp; } /* if */ pointers_block->last_template = tp; tp->next = NULL; /* The parent scope pointer is weird for templates, since each declaration gets a separate template entry that goes on the list wherever it appears. The parent pointer is usually set already, and if so we leave it alone. */ if (parent_scope_of(tp) == NULL && !tp->source_corresp.parent_via_local_scope_ref) { /* The parent_via_local_scope_ref is used to suppress this in certain error cases. */ set_parent_scope(&tp->source_corresp, iek_template, sp); } /* if */ } /* add_to_templates_list */ #if MICROSOFT_EXTENSIONS_ALLOWED void add_to_ms_attributes_list(an_ms_attribute_ptr msap, a_scope_depth scope_depth) /* Add the Microsoft attribute entry pointed to by msap to the indicated scope (or the nearest enclosing scope for template declaration scopes). */ { a_scope_stack_entry_ptr ssep; a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; assert_is_valid_scope_depth(scope_depth); ssep = &scope_stack[scope_depth]; /* Skip template declaration and instantiation context scopes. */ while (scope_is(ssep, sck_template_declaration) || scope_is(ssep, sck_instantiation_context)) { ssep = previous_scope_of(ssep); } /* while */ sp = ensure_il_scope_exists(ssep); check_assertion_str(sp != NULL, "add_to_ms_attributes_list: NULL IL scope"); pointers_block = assoc_pointers_block_of(ssep); if (sp->ms_attributes == NULL) { sp->ms_attributes = msap; } else { pointers_block->last_ms_attribute->next = msap; } /* if */ pointers_block->last_ms_attribute = msap; msap->next = NULL; } /* add_to_ms_attributes_list */ a_routine_ptr selectively_overridden_function(a_routine_ptr rp) /* If rp is a member function that was declared to selectively override a known function, return that overridden function. Otherwise, return NULL. In particular, if rp is a member of a prototype instantiation declared to override a member from a template-dependent (i.e., unknown) base class, NULL will be returned. Selective overriding is a Microsoft extension. For example: struct B1 { virtual int f() = 0; }; struct B2 { virtual int f() = 0; }; struct D: B1, B2 { int B1::f() { return 1; } // Selectively overrides B1::f (not B2::f). int B2::f() { return 2; } // Selectively overrides B2::f (not B1::f). }; */ { a_routine_ptr result = NULL; if (rp->overridden_functions != NULL) { a_tagged_pointer ep = rp->overridden_functions->entity; if ((an_il_entry_kind)ep.kind == iek_routine) { result = (a_routine_ptr)ep.ptr; } /* if */ check_assertion(rp->overridden_functions->next == NULL); } /* if */ return result; } /* selectively_overridden_function */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if GENERATE_MICROSOFT_IF_EXISTS_ENTRIES void add_to_ms_if_exists_list(an_ms_if_exists_ptr msiep, a_scope_depth scope_depth) /* Add the Microsoft __if_exists entry pointed to by msiep to the indicated scope. */ { a_scope_stack_entry_ptr ssep; a_scope_ptr sp; a_scope_pointers_block_ptr pointers_block; assert_is_valid_scope_depth(scope_depth); ssep = &scope_stack[scope_depth]; sp = ensure_il_scope_exists(ssep); check_assertion_str(sp != NULL, "add_to_if_exists_list: NULL IL scope"); pointers_block = assoc_pointers_block_of(ssep); if (sp->ms_if_exists == NULL) { sp->ms_if_exists = msiep; } else { pointers_block->last_ms_if_exists->next = msiep; } /* if */ pointers_block->last_ms_if_exists = msiep; msiep->next = NULL; } /* add_to_ms_if_exists_list */ #endif /* GENERATE_MICROSOFT_IF_EXISTS_ENTRIES */ #if RECORD_MACROS_IN_IL void add_to_macros_list(a_macro_ptr mp) /* Add the IL macro entry pointed to by mp to the list for the file scope. */ { if (il_header.macros == NULL) { il_header.macros = mp; } else { curr_translation_unit->last_macro->next = mp; } /* if */ curr_translation_unit->last_macro = mp; } /* add_to_macros_list */ #endif /* RECORD_MACROS_IN_IL */ static unsigned long num_file_scope_entities_with_assoc_pragmas( a_scope_ptr scope) /* Return the number of local static variables and local types with associated pragmas in the indicated scope (a function, block, or local class scope) and its subscopes. */ { unsigned long count = 0; a_variable_ptr variable; a_type_ptr type; a_scope_ptr sub_scope; for (variable = scope->variables; variable != NULL; variable = variable->next) { if (variable->source_corresp.has_associated_pragma) { count++; } /* if */ } /* for */ for (type = scope->types; type != NULL; type = type->next) { if (type->source_corresp.has_associated_pragma) { count++; } /* if */ if (!C_mode() && is_immediate_class_type(type)) { sub_scope = type->variant.class_struct_union.extra_info->assoc_scope; if (!scope_is_null_or_placeholder(sub_scope)) { count += num_file_scope_entities_with_assoc_pragmas(sub_scope); } /* if */ } /* if */ } /* if */ for (sub_scope = scope->scopes; sub_scope != NULL; sub_scope = sub_scope->next) { count += num_file_scope_entities_with_assoc_pragmas(sub_scope); } /* for */ return count; } /* num_file_scope_entities_with_assoc_pragmas */ void eliminate_pragmas_for_file_scope_entities(a_scope_ptr scope) /* The indicated scope is the function scope of a function that is being eliminated. Eliminate any pragmas that reference its local static variables or local classes. */ { unsigned long count = num_file_scope_entities_with_assoc_pragmas(scope); if (count > 0) { a_translation_unit_ptr tup = trans_unit_for_scope[scope->number]; a_scope_ptr primary_scope = tup->primary_scope; a_pragma_ptr pp, prev_pp; for (prev_pp = NULL, pp = primary_scope->pragmas; pp != NULL; pp = pp->next) { a_boolean remove_entry = FALSE; if ((an_il_entry_kind)pp->entity.kind == iek_variable) { a_variable_ptr variable = (a_variable_ptr)pp->entity.ptr; if (variable->source_corresp.is_local_to_function && /* find_scope_of_variable finds the variable only if it is in the hierarchy of scopes, so it can be used to test whether the variable is in the given function. */ find_scope_of_variable(variable, scope) != NULL) { /* This pragma applies to a local static of the function. */ remove_entry = TRUE; } /* if */ } else if ((an_il_entry_kind)pp->entity.kind == iek_type) { a_type_ptr type = (a_type_ptr)pp->entity.ptr; if (type->source_corresp.is_local_to_function && /* find_scope_of_type finds the type only if it is in the hierarchy of scopes, so it can be used to test whether the type is in the given function. */ find_scope_of_type(type, scope) != NULL) { /* This pragma applies to a local type of the function. */ remove_entry = TRUE; } /* if */ } /* if */ if (remove_entry) { /* Unlink the pragma. */ if (prev_pp == NULL) { il_header.primary_scope->pragmas = pp->next; } else { prev_pp->next = pp->next; } /* if */ if (pp->next == NULL) { tup->file_scope_pointers_block.last_pragma = prev_pp; } /* if */ count--; if (count == 0) break; } else { prev_pp = pp; } /* if */ } /* for */ } /* if */ } /* eliminate_pragmas_for_file_scope_entities */ void clear_function_body(a_scope_ptr sp) /* sp is the scope for a routine. Eliminate the routine definition by resetting the routine entry to an undefined state and freeing the associated memory region. Note that this does not eliminate any associated scope orphaned list header entry -- any such entry should either be not created in the first place (as in pop_scope) or removed by a separate pass (as in eliminate_unneeded_scope_orphaned_list_entries). */ { a_routine_ptr rp = sp->variant.routine.ptr; a_memory_region_number n; #if DEBUG if (debug_level >= 3 || db_trace("dump_elim", rp, iek_routine)) { fprintf(f_debug, "Removing function body for "); db_name_full(&rp->source_corresp, iek_routine); fputc('\n', f_debug); } /* if */ #endif /* DEBUG */ /* Reset the routine entry to undefined state. (Note that the corresponding symbol remains marked as "defined" so that duplicate definitions can be caught.) */ #if GENERATE_SOURCE_SEQUENCE_LISTS eliminate_function_body_source_sequence_entries(sp); #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ if (vla_enabled) { /* Any vla-dimension entries associated with this routine will point to file-scope types that are flagged as having an associated vla-dimension. Clear those flags. */ a_vla_dimension_ptr vdp; for (vdp = sp->vla_dimensions; vdp != NULL; vdp = vdp->next) { vdp->type->variant.array.has_assoc_vla_dimension = FALSE; } /* for */ } /* if */ #if !SCOPE_ORPHANED_LIST_PROCESSING_NEEDED /* If scope orphaned lists are maintained, the local statics and local classes can stay around even if the function is eliminated. */ eliminate_pragmas_for_file_scope_entities(sp); #endif /* !SCOPE_ORPHANED_LIST_PROCESSING_NEEDED */ rp->defined = FALSE; rp->defined_in_friend_decl = FALSE; il_header.function_def_table[rp->function_def_number].scope = NULL; il_header.function_def_table[rp->function_def_number].memory_region = NULL_region_number; rp->function_def_number = NULL_function_def_number; rp->is_delegating_ctor = FALSE; skip_typerefs(rp->type)->variant.routine.extra_info->assoc_routine = NULL; if (rp->storage_class == (a_storage_class)sc_unspecified) { rp->storage_class = (a_storage_class)sc_extern; #if IA64_ABI && DO_IL_LOWERING rp->use_comdat = FALSE; #endif /* IA64_ABI && DO_IL_LOWERING */ } /* if */ rp->need_out_of_line_copy = FALSE; #if DO_IL_LOWERING && MINIMAL_INLINING /* If IL lowering was done and these flags are set, clear them to avoid problems later. */ rp->inlinable = FALSE; #endif /* DO_IL_LOWERING && MINIMAL_INLINING */ /* Note that defined_outside_of_parent is not reset. The main reason for this is the fact that the flag is needed for prototype instantiations whose bodies are discarded. */ /* Unlink the scope from the memory region list. */ n = rp->memory_region; if (n != NULL_region_number) { if (sp->prev == NULL) { il_header.region_scope_entry[n] = sp->next; } else { sp->prev->next = sp->next; } /* if */ if (sp->next != NULL) { sp->next->prev = sp->prev; } /* if */ if (il_header.region_scope_entry[n] == NULL) { /* If we removed the last function in the memory region, free the region. */ free_memory_region(n); } /* if */ } /* if */ rp->memory_region = NULL_region_number; } /* clear_function_body */ void detach_from_object_lifetime_tree(an_object_lifetime_ptr olp) /* Unlink the object lifetime entry pointed to by olp from the child-lifetime list of its parent. If olp is NULL, do nothing. */ { if (olp != NULL) { an_object_lifetime_ptr parent, child, prev_child; parent = olp->parent_lifetime; /* This doesn't support removing function object lifetimes. */ check_assertion(parent != NULL); prev_child = NULL; child = parent->child_lifetime; while (child != olp) { check_assertion(child != NULL); prev_child = child; child = child->next; } /* while */ if (prev_child == NULL) { parent->child_lifetime = olp->next; } else { prev_child->next = olp->next; } /* if */ olp->parent_lifetime = NULL; olp->next = NULL; olp->parent_destruction_sublist = NULL; #if DEBUG if (debug_level >= 4) { fputs("lifetime unlinked:\n", f_debug); db_object_lifetime(olp); db_object_lifetime(parent); } /* if */ #endif /* DEBUG */ } /* if */ } /* detach_from_object_lifetime_tree */ #if DEBUG void db_class_list(a_class_list_entry_ptr list) /* Display a class list for debugging purposes. */ { if (list == NULL) { fprintf(f_debug, " \n"); } else { a_boolean secondary = in_secondary_trans_unit(list); a_class_list_entry_ptr entry; for (entry = list; entry != NULL; entry = entry->next) { if (secondary != in_secondary_trans_unit(entry)) { (void)fprintf(f_debug, " ***switch between translation units***\n"); secondary = !secondary; } /* if */ fprintf(f_debug, " "); db_abbreviated_type(entry->class_type); fprintf(f_debug, "\n"); } /* for */ } /* if */ } /* db_class_list */ void db_classes_in_list(an_il_entity_list_entry_ptr list) /* Display a class list for debugging purposes. */ { auto is_class = [](an_il_entity_list_entry_ptr lep) -> a_boolean { return (lep->entity.kind == iek_type && is_class_struct_union_type((a_type_ptr)lep->entity.ptr)); }; size_t num_classes = count_list_elements(list, is_class); if (num_classes == 0) { fprintf(f_debug, " \n"); } else { an_il_entity_list_entry_ptr entry = list; a_boolean secondary = FALSE; a_boolean first = TRUE; for (; entry != NULL; entry = entry->next) { if (!is_class(entry)) { continue; } /* if */ if (first) { secondary = in_secondary_trans_unit(list); first = FALSE; } else if (secondary != in_secondary_trans_unit(entry)) { (void)fprintf(f_debug, " ***switch between translation units***\n"); secondary = !secondary; } /* if */ fprintf(f_debug, " "); db_abbreviated_type((a_type_ptr)entry->entity.ptr); fprintf(f_debug, "\n"); } /* for */ } /* if */ } /* db_classes_in_list */ #endif /* DEBUG */ #if MAINTAIN_NEEDED_FLAGS static void eliminate_references_from_befriended_entities( a_type_ptr class_type) /* class_type is a class whose definition is being eliminated or that is being removed from the IL altogether. In either case, if it has any friend declarations (classes or functions), those entities will have pointers back to class_type. Those back-pointers should be cleared. In the process, the friends pointer in class_type's type supplement will also be cleared. */ { a_class_type_supplement_ptr ctsp; a_class_list_entry_ptr clep, prev_clep, next_clep; db_enter(4, "eliminate_references_from_befriended_entities"); ctsp = class_type->variant.class_struct_union.extra_info; /* Go through the befriended entities. */ while (ctsp->friends != NULL) { if (ctsp->friends->entity.kind == iek_type) { a_type_ptr friend_class = (a_type_ptr)ctsp->friends->entity.ptr; /* The only expected types are class types. */ check_assertion(is_class_struct_union_type(friend_class)); a_class_type_supplement_ptr friend_ctsp = friend_class->variant.class_struct_union.extra_info; if (friend_ctsp->removed_from_il) { /* This class was removed from the IL already. */ check_assertion(!il_entry_prefix_of(friend_class).keep_in_il); #if DEBUG if (debug_level >= 4 || db_trace("dump_elim", friend_class, iek_type)) { fputs(" Befriended ", f_debug); db_abbreviated_type(friend_class); fputs(" is already eliminated", f_debug); fputc('\n', f_debug); } /* if */ #endif /* DEBUG */ } else { /* Go through the list of classes that have specified friend_class as a friend, find the entry that matches class_type, and link around it. */ prev_clep = NULL; clep = friend_ctsp->befriending_classes; for (; clep != NULL; clep = next_clep) { next_clep = clep->next; if (clep->class_type == class_type) { /* A match -- link around it. */ if (prev_clep == NULL) { friend_ctsp->befriending_classes = next_clep; } else { prev_clep->next = next_clep; } /* if */ #if DEBUG if (debug_level >= 4 || db_trace("dump_elim", friend_class, iek_type) || db_trace("friendship", friend_class, iek_type)) { db_type_name(friend_class); fputs(" no longer befriended by ", f_debug); db_type_name(class_type); fputc('\n', f_debug); if (db_flag_is_set("friendship")) { fprintf(f_debug, "befriending_classes of friend class:\n"); db_class_list(friend_ctsp->befriending_classes); } /* if */ } /* if */ #endif /* DEBUG */ /* Break out of the inner loop and continue the outer loop, moving to the next class declared as a friend of class_type. */ break; } /* if */ /* No match -- keep looping. */ prev_clep = clep; } /* for */ #if CHECKING if (clep == NULL) { #if DEBUG fprintf(f_debug, "class type: "); db_abbreviated_type(class_type); fprintf(f_debug, "\nfriend class: "); db_abbreviated_type(friend_class); fprintf(f_debug, "\n"); fprintf(f_debug, "befriending_classes of friend class:\n"); db_class_list(friend_ctsp->befriending_classes); #endif /* DEBUG */ unexpected_condition_str2( "eliminate_references_from_befriended_entities", "class not found among befriending_classes of friend class"); } /* if */ #endif /* CHECKING */ } /* if */ } else if (ctsp->friends->entity.kind == iek_routine) { a_routine_ptr friend_rout = (a_routine_ptr)ctsp->friends->entity.ptr; /* Go through the list of classes that have specified friend_rout as a friend, find the entry that matches class_type, and link around it. */ prev_clep = NULL; clep = rout_befriending_classes(friend_rout); check_assertion(!(friend_rout->is_inheriting_ctor && clep != NULL)); for (; clep != NULL; clep = next_clep) { next_clep = clep->next; if (clep->class_type == class_type) { /* A match -- link around it. */ #if DEBUG if (debug_level >= 4 || db_trace("dump_elim", friend_rout, iek_routine) || db_trace("friendship", friend_rout, iek_routine)) { fputs("Routine ", f_debug); db_name_full(&friend_rout->source_corresp, iek_routine); fputs(" no longer befriended by ", f_debug); db_type_name(class_type); fputc('\n', f_debug); if (db_flag_is_set("friendship")) { fprintf(f_debug, "befriending_classes of friend routine:\n"); db_class_list(rout_befriending_classes(friend_rout)); } /* if */ } /* if */ #endif /* DEBUG */ if (prev_clep == NULL) { friend_rout->friends_or_originator.befriending_classes = next_clep; } else { prev_clep->next = next_clep; } /* if */ /* Break out of the inner loop and continue the outer loop, moving to the next routine declared as a friend of class_type. */ break; } /* if */ /* No match -- keep looping. */ prev_clep = clep; } /* for */ #if CHECKING if (clep == NULL) { #if DEBUG fprintf(f_debug, "class type: "); db_abbreviated_type(class_type); fprintf(f_debug, "\nfriend rout: "); db_name_full(&friend_rout->source_corresp, iek_routine); fprintf(f_debug, "\n"); fprintf(f_debug, "befriending_classes of friend rout:\n"); db_class_list(rout_befriending_classes(friend_rout)); #endif /* DEBUG */ unexpected_condition_str2( "eliminate_references_from_befriended_entities", "class not found among befriending_classes of friend routine"); } /* if */ #endif /* CHECKING */ } else if (ctsp->friends->entity.kind == iek_template) { a_template_ptr friend_template = (a_template_ptr)ctsp->friends->entity.ptr; a_template_symbol_supplement_ptr friend_tssp = template_supplement_for_template(friend_template); /* Go through the list of classes that have specified friend_template as a friend, find the entry that matches class_type, and link around it. */ prev_clep = NULL; clep = friend_tssp->befriending_classes; for (; clep != NULL; clep = next_clep) { next_clep = clep->next; if (clep->class_type == class_type) { /* A match -- link around it. */ if (prev_clep == NULL) { friend_tssp->befriending_classes = next_clep; } else { prev_clep->next = next_clep; } /* if */ #if DEBUG if (debug_level >= 4 || db_trace("dump_elim", friend_template, iek_template)) { db_template_name(friend_template); fputs(" no longer befriended by ", f_debug); db_type_name(class_type); fputc('\n', f_debug); if (db_flag_is_set("friendship")) { fprintf(f_debug, "befriending_classes of friend class:\n"); db_class_list(friend_tssp->befriending_classes); } /* if */ } /* if */ #endif /* DEBUG */ /* Break out of the inner loop and continue the outer loop, moving to the next class declared as a friend of class_type. */ break; } /* if */ /* No match -- keep looping. */ prev_clep = clep; } /* for */ #if CHECKING if (clep == NULL) { #if DEBUG fprintf(f_debug, "class type: "); db_abbreviated_type(class_type); fprintf(f_debug, "\nfriend template: "); db_template_name(friend_template); fprintf(f_debug, "\n"); fprintf(f_debug, "befriending_classes of friend template:\n"); db_class_list(friend_tssp->befriending_classes); #endif /* DEBUG */ unexpected_condition_str2( "eliminate_references_from_befriended_entities", "class not found among befriending_classes of friend template"); } /* if */ #endif /* CHECKING */ } else { unexpected_condition_str2( "eliminate_references_from_befriended_entities:", "unexpected entity kind"); } /* if */ /* Check the next friend. */ ctsp->friends = ctsp->friends->next; } /* while */ db_exit(); } /* eliminate_references_from_befriended_entities */ static a_boolean ttt_elim_def_arg_lifetimes( a_type_ptr type_ptr, ARG_UNUSED a_boolean *force_end_of_traversal) /* This is a service function designed to be called from traverse_type_tree (whence the ttt_ prefix). It eliminates any object lifetimes associated with routine types encountered. It always returns FALSE, because the point of the traversal is to remove the lifetimes, not to determine some attribute of the type tree. */ { if (type_ptr->kind == (a_type_kind)tk_routine) { a_param_type_ptr ptp; an_expr_node_ptr def_arg_expr; an_object_lifetime_ptr olp; for (ptp = type_ptr->variant.routine.extra_info->param_type_list; ptp != NULL; ptp = ptp->next) { def_arg_expr = ptp->default_arg_expr; if (def_arg_expr != NULL && def_arg_expr->kind == (an_expr_node_kind)enk_object_lifetime) { olp = def_arg_expr->variant.object_lifetime.ptr; check_assertion(olp != NULL); detach_from_object_lifetime_tree(olp); #if DEBUG if (debug_level >= 3 || db_flag_is_set("dump_elim")) { fputs("Unlinking default arg object lifetime\n", f_debug); } /* if */ #endif /* DEBUG */ /* Avoid processing this default argument more than once. */ ptp->default_arg_expr = NULL; } /* if */ } /* for */ } /* if */ return FALSE; } /* ttt_elim_def_arg_lifetimes */ void eliminate_default_arg_object_lifetimes(a_type_ptr type) /* A routine or variable is being eliminated from the IL. "type" is its type. Be sure that any object lifetimes created for any default arguments have been removed, too. Note that default arguments can come up at non-top-level parts of types in things like pointers to functions. */ { /* Default arguments come up only in C++ mode. */ if (!C_mode()) { (void)traverse_type_tree(type, ttt_elim_def_arg_lifetimes, (TTT_RETURN_TYPE | TTT_PARAM_TYPES | TTT_SKIP_TYPEREFS | TTT_STOP_AT_TYPEDEFS)); } /* if */ } /* eliminate_default_arg_object_lifetimes */ void eliminate_routine_default_arg_object_lifetimes(a_routine_ptr rout) /* The indicated routine is being eliminated from the IL. Be sure that any object lifetimes created for its default arguments have been removed, too. */ { eliminate_default_arg_object_lifetimes(rout->type); #if GENERATE_SOURCE_SEQUENCE_LISTS if (rout->declared_type != rout->type && rout->declared_type != NULL) { eliminate_default_arg_object_lifetimes(rout->declared_type); } /* if */ #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ } /* eliminate_routine_default_arg_object_lifetimes */ void eliminate_variable_default_arg_object_lifetimes(a_variable_ptr var) /* The indicated variable is being eliminated from the IL. Be sure that any object lifetimes created for its default arguments have been removed, too. Default arguments can come up in variables that are, for example, pointers to functions. */ { eliminate_default_arg_object_lifetimes(var->type); #if GENERATE_SOURCE_SEQUENCE_LISTS if (var->declared_type != var->type && var->declared_type != NULL) { eliminate_default_arg_object_lifetimes(var->declared_type); } /* if */ #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ } /* eliminate_variable_default_arg_object_lifetimes */ static void eliminate_member_function_default_arg_object_lifetimes( a_type_ptr class_type) /* Either class_type itself or its definition is being eliminated. Be sure that any object lifetimes created for default arguments of its member functions have been removed from the IL. */ { a_scope_ptr sp; a_routine_ptr rp; /* Get the scope associated with this class. */ sp = class_type->variant.class_struct_union.extra_info->assoc_scope; if (!scope_is_null_or_placeholder(sp)) { /* Traverse its member function list. */ for (rp = sp->routines; rp != NULL; rp = rp->next) { eliminate_routine_default_arg_object_lifetimes(rp); } /* for */ #if GENERATE_SOURCE_SEQUENCE_LISTS /* Check the default arguments on the declared-type recorded in secondary-decl source sequence entries, too. */ { a_source_sequence_entry_ptr ssep; a_src_seq_secondary_decl_ptr sssdp; an_il_entry_kind kind; /* Traverse the section of the source-sequence list associated with the body of the class. */ ssep = class_type->source_corresp.source_sequence_entry; if (ssep != NULL) { ssep = ssep->next; for (; ssep != NULL; ssep = ssep->next) { check_assertion(ssep != NULL); kind = (an_il_entry_kind)ssep->entity.kind; if (kind == (an_il_entry_kind)iek_src_seq_end_of_construct) { if (ss_entry_ptr(ssep, a_src_seq_end_of_construct_ptr)-> entity.ptr == (char *)class_type) { /* Last of the source sequence entries belonging to the class body. */ break; } /* if */ } else if (kind == (an_il_entry_kind)iek_src_seq_secondary_decl) { sssdp = ss_entry_ptr(ssep, a_src_seq_secondary_decl_ptr); if ((an_il_entry_kind)sssdp->entity.kind == (an_il_entry_kind)iek_routine && sssdp->declared_type != NULL) { eliminate_default_arg_object_lifetimes(sssdp->declared_type); } /* if */ } /* if */ } /* for */ } /* if */ } #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ } /* if */ } /* eliminate_member_function_default_arg_object_lifetimes */ static void clear_variable_instantiation_required(a_variable_ptr vp) /* The indicated variable is not needed (and may be removed from the IL). If it is a template, clear its instantiation required information. */ { if (vp->is_template_variable && !vp->is_specialized && !vp->is_prototype_instantiation) { #if MICROSOFT_EXTENSIONS_ALLOWED if (vp->source_corresp.is_class_member && (scp_parent_class(&(vp->source_corresp))-> variant.class_struct_union.is_generic_instance || scp_parent_class(&(vp->source_corresp))-> variant.class_struct_union.is_generic_definition)) { /* No need to clear the instantiation required flag on a generic static data member. */ } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here. */ { a_symbol_ptr sym; sym = (a_symbol_ptr)vp->source_corresp.assoc_info; if (sym != NULL) { set_instance_required(sym, FALSE, SIR_CLEAR_VALUE); } /* if */ } /* if */ } /* if */ } /* clear_variable_instantiation_required */ static void clear_routine_instantiation_required(a_routine_ptr rp) /* The indicated routine is not needed (and may be removed from the IL). If it is a template, clear its instantiation required information. */ { /* Don't attempt to do this for prototype instantiations, which are not put on the instantiation required list. */ if ((rp->is_template_function && !rp->is_specialized) || (instantiate_extern_inline && rp->is_inline)) { if (!rp->is_prototype_instantiation #if MICROSOFT_EXTENSIONS_ALLOWED && !(rp->is_generic_definition || rp->is_generic_instance) #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ ) { a_symbol_ptr sym; sym = (a_symbol_ptr)rp->source_corresp.assoc_info; if (sym != NULL) { set_instance_required(sym, FALSE, SIR_CLEAR_VALUE); } /* if */ } /* if */ } /* if */ } /* clear_routine_instantiation_required */ static void clear_instantiation_information_for_eliminated_members( a_type_ptr class_type) /* The indicated class type or its definition is being eliminated from the IL, which means the class members are also being eliminated. Do any necessary processing on those members to clear instantiation information. */ { a_class_type_supplement_ptr ctsp = class_type_supp(class_type); check_assertion(!C_mode() && ctsp != NULL); if (!scope_is_null_or_placeholder(ctsp->assoc_scope)) { a_scope_ptr scope = ctsp->assoc_scope; /* Only check the members if there might be some templates. Suppress this for prototype instantiations and generics, which will not have members on the instantiation required list. */ if ((class_type->variant.class_struct_union.is_template_class || scope->templates != NULL) && #if MICROSOFT_EXTENSIONS_ALLOWED !(class_type->variant.class_struct_union.is_generic_instance || class_type->variant.class_struct_union.is_generic_definition) && #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ !class_type->variant.class_struct_union.is_prototype_instantiation) { a_variable_ptr vp; a_routine_ptr rp; for (vp = scope->variables; vp != NULL; vp = vp->next) { clear_variable_instantiation_required(vp); } /* for */ for (rp = scope->routines; rp != NULL; rp = rp->next) { clear_routine_instantiation_required(rp); } /* for */ } /* if */ } /* if */ } /* clear_instantiation_information_for_eliminated_members */ static void process_members_of_eliminated_class_definition( a_type_ptr class_type) /* The indicated class type or its definition is being eliminated from the IL, which means the class members are also being eliminated. Do any necessary processing on those members. */ { a_class_type_supplement_ptr ctsp = class_type_supp(class_type); check_assertion(!C_mode() && ctsp != NULL); /* If the definition of class_type included friend declarations, the befriended classes and routines have pointers back to class_type. Those pointers have to be removed. */ eliminate_references_from_befriended_entities(class_type); /* Remove any object lifetimes that may be associated with default arguments of its member functions. */ eliminate_member_function_default_arg_object_lifetimes(class_type); /* Clear instantiation information for any template members. */ clear_instantiation_information_for_eliminated_members(class_type); /* Do the same processing for nested classes. */ if (!scope_is_null_or_placeholder(ctsp->assoc_scope)) { a_type_ptr tp = ctsp->assoc_scope->types; for (; tp != NULL; tp = tp->next) { if (is_immediate_class_type(tp)) { process_members_of_eliminated_class_definition(tp); } /* if */ } /* for */ } /* if */ } /* process_members_of_eliminated_class_definition */ static void turn_class_definition_into_declaration(a_type_ptr class_type) /* class_type identifies a class whose definition is not needed. Turn the IL entry into one representing a nondefining declaration. */ { db_enter(4, "turn_class_definition_into_declaration"); #if DEBUG if (debug_level >= 3 || db_trace("dump_elim", class_type, iek_type)) { fputs("Removing definition of ", f_debug); db_abbreviated_type(class_type); fputc('\n', f_debug); } /* if */ #endif /* DEBUG */ #if CHECKING if (class_definition_needed_flag_is_set(class_type)) { #if DEBUG fprintf(f_debug, "Class type: "); db_abbreviated_type(class_type); fprintf(f_debug, "\n"); #endif /* DEBUG */ internal_error("turn_class_definition_into_declaration: class def needed"); } /* if */ #endif /* CHECKING */ if (!C_mode()) { /* Do any necessary processing on the members of the class, which are being eliminated because the class is being eliminated. */ process_members_of_eliminated_class_definition(class_type); } /* if */ #if GENERATE_SOURCE_SEQUENCE_LISTS eliminate_class_body_source_sequence_entries(class_type); #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ /* Clear the fields of the class type that are only meaningful for a defined class. */ clear_class_type_definition_fields(class_type); db_exit(); } /* turn_class_definition_into_declaration */ static void eliminate_unneeded_class_definitions(a_type_ptr class_type) /* This routine is part of the processing that prunes the IL based on how the keep_in_il and keep_definition_in_il flags are set on various entries. class_type specifies a class which is to be kept in the IL but whose definition (if there is one) may not need to be retained. If the definition is not needed, transform this entry to represent a non-defining declaration of the class. */ { db_enter(4, "eliminate_unneeded_class_definitions"); /* Eliminate the class definition if appropriate. */ if (class_type->incomplete) { /* There is no definition to eliminate. */ } else if (!class_type->variant.class_struct_union.keep_definition_in_il) { turn_class_definition_into_declaration(class_type); } else { /* The class definition is to be kept. Process nested classes. */ if (!C_mode()) { a_class_type_supplement_ptr ctsp = class_type_supp(class_type); /* Note that even though class_type is complete, it may not have an associated scope (e.g., in the case of some nonreal classes). */ if (!scope_is_null_or_placeholder(ctsp->assoc_scope)) { a_type_ptr tp = ctsp->assoc_scope->types; for (; tp != NULL; tp = tp->next) { if (is_immediate_class_type(tp)) { eliminate_unneeded_class_definitions(tp); } /* if */ } /* for */ } /* if */ } /* if */ } /* if */ db_exit(); } /* eliminate_unneeded_class_definitions */ void eliminate_bodies_of_unneeded_functions(void) /* Go through all the function definitions looking for those associated with routines that are not needed. Eliminate the body -- the IL scope entry and everything dependent on it. The routine entry itself is dealt with later. */ { a_function_def_number n; a_scope_ptr sp; db_enter(3, "eliminate_bodies_of_unneeded_functions"); /* Loop through the memory regions. Skip the front end and file scope memory regions. */ for (n = 1; n <= highest_used_function_def_number; ++n) { a_memory_region_number mrn = mem_region_for_function_def(n); if (mem_region_table[mrn] == NULL) { /* This memory has already been freed. */ } else { sp = scope_for_function_def(n); /* Skip memory regions that aren't in the current translation unit, and file scope memory regions for secondary translation units. */ if (sp != NULL && ((curr_translation_unit == translation_units) ? !in_secondary_trans_unit(sp) : (trans_unit_for_scope[sp->number] == curr_translation_unit))) { a_routine_ptr rp; a_boolean function_body_processing_finished; check_assertion(sp->kind == (a_scope_kind)sck_function); rp = sp->variant.routine.ptr; /* Do not clear the body of a function whose processing has not yet finished. For functions that are not top-level functions in their memory regions (currently, the members of local lambdas), do not clear the body if the corresponding top-level function's processing is not done yet. */ if (rp->is_top_level_in_mem_region) { function_body_processing_finished = sp->function_body_processing_finished; } else { function_body_processing_finished = il_header.region_scope_entry[mrn] ->function_body_processing_finished; } /* if */ if (!rp->keep_definition_in_il && function_body_processing_finished) { /* An unneeded routine definition. Delete it. */ clear_function_body(sp); } /* if */ } /* if */ } /* if */ } /* for */ db_exit(); } /* eliminate_bodies_of_unneeded_functions */ static a_boolean type_is_to_be_kept_in_il(a_type_ptr tp) /* Return TRUE if the indicated type is to be kept in the IL. For the most part, this means that its keep_in_il flag is TRUE. */ { a_boolean keep; a_type_ptr type = tp; while (type->kind == (a_type_kind)tk_typeref && !typeref_is_typedef(type)) { type = type->variant.typeref.type; } /* while */ keep = il_entry_prefix_of(type).keep_in_il; return keep; } /* type_is_to_be_kept_in_il */ #if SCOPE_ORPHANED_LIST_PROCESSING_NEEDED void eliminate_unneeded_scope_orphaned_list_entries(void) /* Remove scope-orphaned-list headers that are associated with routines whose bodies have been eliminated. It would seem that one could simply remove those headers, but sometimes they must be retained because types, namespace aliases, or variables on the lists have been marked as keep_in_il because, for example, they appear on orphan lists. */ { a_scope_orphaned_list_header_ptr solhp, prev_solhp, next_solhp; a_routine_ptr rp; a_variable_ptr vp, prev_vp, next_vp; a_type_ptr tp, prev_tp, next_tp; a_namespace_ptr na, prev_na, next_na; prev_solhp = NULL; for (solhp = il_header.scope_orphaned_list_headers; solhp != NULL; solhp = next_solhp) { next_solhp = solhp->next; rp = solhp->assoc_routine; /* Look for a routine whose body has been eliminated. */ if (rp->function_def_number != NULL_function_def_number) { /* This one has not been eliminated. */ prev_solhp = solhp; } else { /* This one has. First traverse the variables list. If any variables on the orphaned list are marked "keep_in_il", the orphaned-list header itself has to be kept, too. */ prev_vp = NULL; for (vp = solhp->orphaned_variables; vp != NULL; vp = next_vp) { next_vp = vp->next; #if DEBUG if (debug_level >= 3 || db_trace("dump_elim", vp, iek_variable)) { fprintf(f_debug, "%semoving orphaned variable ", il_entry_prefix_of(vp).keep_in_il ? "Not r" : "R"); db_name_full(&vp->source_corresp, iek_variable); fputc('\n', f_debug); } /* if */ #endif /* DEBUG */ if (!il_entry_prefix_of(vp).keep_in_il) { /* Remove it from the variables list by linking around it. */ if (prev_vp == NULL) { solhp->orphaned_variables = vp->next; } else { prev_vp->next = vp->next; } /* if */ vp->next = NULL; } else { prev_vp = vp; } /* if */ } /* for */ /* Traverse the types list. */ prev_tp = NULL; for (tp = solhp->orphaned_types; tp != NULL; tp = next_tp) { a_boolean keep; next_tp = tp->next; keep = type_is_to_be_kept_in_il(tp); #if DEBUG if (debug_level >= 3 || db_trace("dump_elim", tp, iek_type)) { fprintf(f_debug, "%semoving orphaned type ", keep ? "Not r" : "R"); db_abbreviated_type(tp); fputc('\n', f_debug); } /* if */ #endif /* DEBUG */ if (!keep) { /* Remove it from the types list by linking around it. */ if (prev_tp == NULL) { solhp->orphaned_types = tp->next; } else { prev_tp->next = tp->next; } /* if */ tp->next = NULL; } else { prev_tp = tp; } /* if */ } /* for */ /* Traverse the namespace aliases list. */ prev_na = NULL; for (na = solhp->orphaned_namespaces; na != NULL; na = next_na) { next_na = na->next; #if DEBUG if (debug_level >= 3 || db_trace("dump_elim", na, iek_namespace)) { fprintf(f_debug, "%semoving orphaned namespace ", il_entry_prefix_of(na).keep_in_il ? "Not r" : "R"); db_name_full(&vp->source_corresp, iek_namespace); fputc('\n', f_debug); } /* if */ #endif /* DEBUG */ if (!il_entry_prefix_of(na).keep_in_il) { /* Remove it from the namespaces list by linking around it. */ if (prev_na == NULL) { solhp->orphaned_namespaces = na->next; } else { prev_na->next = na->next; } /* if */ na->next = NULL; } else { prev_na = na; } /* if */ } /* for */ #if GENERATE_SOURCE_SEQUENCE_LISTS if (solhp->orphaned_src_seq_sublists != NULL) { /* Whether or not the header itself remains in the IL, the source sequence information associated with the function body is unneeded. First go through all the source sequence entries and clear out pointers from IL entries back to them. */ a_source_sequence_entry_ptr ssep; a_src_seq_sublist_ptr sublist; a_source_correspondence *scp; for (sublist = solhp->orphaned_src_seq_sublists; sublist != NULL; sublist = sublist->next) { for (ssep = sublist->source_sequence_list; ssep != NULL; ssep = ssep->next) { scp = source_corresp_for_il_entry(ssep->entity.ptr, (an_il_entry_kind)ssep-> entity.kind); if (scp != NULL) { check_assertion(scp->source_sequence_entry == ssep || scp->source_sequence_entry == NULL); scp->source_sequence_entry = NULL; } /* if */ } /* for */ } /* for */ /* Now throw away the list. */ solhp->orphaned_src_seq_sublists = NULL; } /* if */ #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ /* Only retain scope-orphaned-list headers for which non-NULL lists remain. */ if (solhp->orphaned_variables != NULL || solhp->orphaned_types != NULL || solhp->orphaned_namespaces != NULL) { prev_solhp = solhp; /* The routine entry (not the body) should already be marked as keep-in-il, because the enclosing_routine pointer of the local entities should point to the enclosing function, so marking a local entity as keep-in-il should mark the routine also. */ check_assertion(il_entry_prefix_of(rp).keep_in_il); } else { /* Unlink it. */ if (prev_solhp == NULL) { il_header.scope_orphaned_list_headers = next_solhp; } else { prev_solhp->next = next_solhp; } /* if */ solhp->next = NULL; } /* if */ } /* if */ } /* for */ curr_translation_unit->last_scope_orphaned_list_header = prev_solhp; } /* eliminate_unneeded_scope_orphaned_list_entries */ #endif /* SCOPE_ORPHANED_LIST_PROCESSING_NEEDED */ #if MAINTAIN_NEEDED_FLAGS void clear_instantiation_required_on_unneeded_entities(a_scope_ptr scope) /* Find any routines or static data members in the specified scope that are unneeded and reset their instantiation required flags if necessary. Used only in C++ mode. */ { a_routine_ptr rp; a_variable_ptr vp; #if !DO_IL_LOWERING a_namespace_ptr nsp; a_type_ptr tp; #endif /* !DO_IL_LOWERING */ check_assertion(!C_mode()); #if !DO_IL_LOWERING /* If lowering has been done there will be no routines in namespaces or classes (they'll all have been promoted to the file scope), so there's no need to do these traversals. */ for (nsp = scope->namespaces; nsp != NULL; nsp = nsp->next) { if (!nsp->is_namespace_alias) { /* Nested namespace scope. */ clear_instantiation_required_on_unneeded_entities( nsp->variant.assoc_scope); } /* if */ } /* for */ for (tp = scope->types; tp != NULL; tp = tp->next) { if (is_immediate_class_type(tp) && #if MICROSOFT_EXTENSIONS_ALLOWED !(tp->variant.class_struct_union.is_generic_instance || tp->variant.class_struct_union.is_generic_definition) && #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ !tp->variant.class_struct_union.is_nonreal_class && class_type_supp(tp)->assoc_scope != NULL) { clear_instantiation_required_on_unneeded_entities( class_type_supp(tp)->assoc_scope); } /* if */ } /* for */ /* Block scopes aren't traversed because there are no local templates, and no templates are allowed in local classes. */ #endif /* !DO_IL_LOWERING */ for (rp = scope->routines; rp != NULL; rp = rp->next) { if (!rp->source_corresp.needed) { /* If the instantiation_required flag was set, clear it now. Or, if the function is extern inline and we are instantiating extern inline functions using a mechanism like the template instantiation mechanism, clear the inline instance required flag. */ if (!rp->is_prototype_instantiation && #if MICROSOFT_EXTENSIONS_ALLOWED !(rp->is_generic_definition || rp->is_generic_instance) && #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ ((rp->is_template_function && !rp->is_specialized) || (instantiate_extern_inline && (rp->is_inline || treat_as_extern_inline(rp))))) { a_symbol_ptr sym; a_boolean okay_to_clear_flag = TRUE; sym = (a_symbol_ptr)rp->source_corresp.assoc_info; if (sym != NULL) { if (rp->is_virtual) { #if DO_IL_LOWERING a_type_ptr class_type = sym_parent_class(sym); a_class_type_supplement_ptr ctsp = class_type_supp(class_type); a_variable_ptr vtbl_var; #if ABI_CHANGES_FOR_RTTI a_variable_ptr typeinfo_var; #endif /* ABI_CHANGES_FOR_RTTI */ if (((vtbl_var = ctsp->virtual_function_table_var) == NULL || !il_entry_prefix_of(vtbl_var).keep_in_il) #if ABI_CHANGES_FOR_RTTI && ((typeinfo_var = class_type->typeinfo_var) == NULL || !il_entry_prefix_of(typeinfo_var).keep_in_il || (typeinfo_uncoupled_when_vtable_is_optional && vtbl_var != NULL && vtbl_var->is_optional_vtable)) #endif /* ABI_CHANGES_FOR_RTTI */ ) { /* Either there is no virtual function table or it's been eliminated from the IL: it's okay to clear the flag, since an otherwise unreferenced virtual function would be needed only if the virtual function table is defined in this translation unit. If the typeinfo variable is kept, keep the virtual function so the virtual function table will be kept so that the typeinfo variable will be kept. However, if the vtable is optional the typeinfo and vtable don't have to go out together, so the state of the typeinfo has no effect. */ } else #endif /* DO_IL_LOWERING */ /* Do not insert code here. */ { /* Virtual function may be needed for defining the virtual function table. */ okay_to_clear_flag = FALSE; } /* if */ } /* if */ #if IA64_ABI && DO_IL_LOWERING if (okay_to_clear_flag) { if (rp->special_kind == (a_special_function_kind)sfk_constructor || rp->special_kind == (a_special_function_kind)sfk_destructor) { /* If an alternate entry point of a constructor or destructor has to be kept, don't clear the instantiation required flag, because it represents all the variants of the constructor or destructor. */ a_routine_list_entry_ptr rlep; for (rlep = rp->variant.ctor_dtor.alternate_entry_points; rlep != NULL; rlep = rlep->next) { if (il_entry_prefix_of(rlep->routine).keep_in_il) { okay_to_clear_flag = FALSE; break; } /* if */ } /* for */ } /* if */ } /* if */ #endif /* IA64_ABI && DO_IL_LOWERING */ if (okay_to_clear_flag) { set_instance_required(sym, FALSE, SIR_CLEAR_VALUE); } /* if */ } /* if */ } /* if */ } /* if */ } /* for */ #if !DO_IL_LOWERING if (scope->kind == (a_scope_kind)sck_file) { /* No need to check for static data members in the file scope if we're not doing lowering. */ } else #endif /* !DO_IL_LOWERING */ /* Do not insert code here. */ { for (vp = scope->variables; vp != NULL; vp = vp->next) { if (!vp->source_corresp.needed && !vp->is_nonreal) { /* If the instantiation_required flag was set, clear it now. The code here is needed when processing lowered IL (the static data members have been promoted out of the class). */ clear_variable_instantiation_required(vp); } /* if */ } /* for */ } /* if */ } /* clear_instantiation_required_on_unneeded_entities */ #endif /* MAINTAIN_NEEDED_FLAGS */ void eliminate_unneeded_il_entries(a_scope_ptr scope) /* Remove selected IL entries from the IL tree. scope is the file scope or a namespace scope; this processing is not done for function or block scopes. Removal is based on how the keep_in_il flag is set for variables, routines, and types. Hidden-name entries and source sequence entries are also eliminated, if appropriate. */ { a_namespace_ptr nsp; a_variable_ptr vp, prev_vp, next_vp; a_type_ptr tp, prev_tp, next_tp; a_routine_ptr rp, prev_rp, next_rp; a_scope_pointers_block_ptr pointers_block; db_enter(3, "eliminate_unneeded_il_entries"); pointers_block = get_pointers_block_for_scope(scope); check_assertion(pointers_block != NULL); /* In C++ process the entities on lists belonging to namespaces defined within the current scope. */ for (nsp = scope->namespaces; nsp != NULL; nsp = nsp->next) { if (!nsp->is_namespace_alias) { /* Nested namespace scope. */ eliminate_unneeded_il_entries(nsp->variant.assoc_scope); } /* if */ } /* for */ /* Go through the list of variables that were declared in the current scope, removing any for which the keep_in_il flag is FALSE. */ prev_vp = NULL; for (vp = scope->variables; vp != NULL; vp = next_vp) { next_vp = vp->next; #if DEBUG if (debug_level >= 3 || db_trace("dump_elim", vp, iek_variable)) { fprintf(f_debug, "%semoving variable ", il_entry_prefix_of(vp).keep_in_il ? "Not r" : "R"); db_name_full(&vp->source_corresp, iek_variable); fputc('\n', f_debug); } /* if */ #endif /* DEBUG */ if (!il_entry_prefix_of(vp).keep_in_il) { /* Remove any object lifetimes that may be associated with its default arguments, e.g., if it's a pointer to function. */ eliminate_variable_default_arg_object_lifetimes(vp); /* Remove it from the variables list by linking around it. */ if (prev_vp == NULL) { scope->variables = vp->next; } else { prev_vp->next = vp->next; } /* if */ vp->next = NULL; } else { prev_vp = vp; } /* if */ } /* for */ pointers_block->last_variable = prev_vp; /* Process the list of routines in a similar way. */ #if SCOPE_ORPHANED_LIST_PROCESSING_NEEDED /* Before eliminating the routines, be sure to remove associated orphaned list entries. */ if (scope->kind == (a_scope_kind)sck_file) { eliminate_unneeded_scope_orphaned_list_entries(); } /* if */ #endif /* SCOPE_ORPHANED_LIST_PROCESSING_NEEDED */ prev_rp = NULL; for (rp = scope->routines; rp != NULL; rp = next_rp) { next_rp = rp->next; #if DEBUG if (debug_level >= 3 || db_trace("dump_elim", rp, iek_routine)) { fprintf(f_debug, "%semoving routine ", il_entry_prefix_of(rp).keep_in_il ? "Not r" : "R"); db_name_full(&rp->source_corresp, iek_routine); fputc('\n', f_debug); } /* if */ #endif /* DEBUG */ if (!il_entry_prefix_of(rp).keep_in_il) { /* Remove any object lifetimes that may be associated with its default arguments. */ eliminate_routine_default_arg_object_lifetimes(rp); /* Remove it from the routines list by linking around it. */ if (prev_rp == NULL) { scope->routines = rp->next; } else { prev_rp->next = rp->next; } /* if */ rp->next = NULL; } else { prev_rp = rp; } /* if */ } /* for */ pointers_block->last_routine = prev_rp; if (il_header.main_routine != NULL && !il_entry_prefix_of(il_header.main_routine).keep_in_il) { /* The routine entry itself will already have been removed from the IL, so remove this reference to it. */ il_header.main_routine = NULL; } /* if */ /* Remove unneeded type entries. This is done after unneeded routine entries have been eliminated since that process may need access to certain class types. */ prev_tp = NULL; for (tp = scope->types; tp != NULL; tp = next_tp) { a_boolean keep; next_tp = tp->next; keep = type_is_to_be_kept_in_il(tp); #if DEBUG if (debug_level >= 3 || db_trace("dump_elim", tp, iek_type)) { fprintf(f_debug, "%semoving ", keep ? "Not r" : "R"); if (has_name(tp)) { db_type_name(tp); } else { db_abbreviated_type(tp); } /* if */ fputc('\n', f_debug); } /* if */ #endif /* DEBUG */ if (!keep) { /* Remove it from the types list by linking around it. */ if (prev_tp == NULL) { scope->types = tp->next; } else { prev_tp->next = tp->next; } /* if */ tp->next = NULL; if (is_immediate_class_type(tp)) { if (!C_mode()) { /* Process the members, which are being removed because the class is being removed. */ process_members_of_eliminated_class_definition(tp); } /* if */ /* This is a class type that has been removed from the IL (because it's not really needed anywhere), but just in case there's a reference to it somewhere that causes it to be written, clear its pointers so they can't be walked. */ tp->variant.class_struct_union.field_list = NULL; clear_class_type_supplement(tp->variant.class_struct_union.extra_info); tp->variant.class_struct_union.extra_info->removed_from_il = TRUE; } /* if */ } else { /* The type is being kept. Check for nested classes. */ if (is_immediate_class_type(tp)) { eliminate_unneeded_class_definitions(tp); } /* if */ prev_tp = tp; } /* if */ } /* for */ pointers_block->last_type = prev_tp; #if RECORD_HIDDEN_NAMES_IN_IL /* Hidden name table entries need not be kept in the IL if they refer to entities that do not need to be kept. */ { a_hidden_name_ptr hnp, prev_hnp = NULL, next_hnp; for (hnp = scope->hidden_names; hnp != NULL; hnp = next_hnp) { next_hnp = hnp->next; #if DEBUG if (debug_level >= 3 || db_trace("dump_elim", hnp->entity.ptr, (an_il_entry_kind)hnp->entity.kind)) { fprintf(f_debug, "%semoving hidden name entry for ", il_entry_prefix_of(hnp->entity.ptr).keep_in_il ? "Not r" : "R"); if (hnp->entity.kind == iek_type) { db_abbreviated_type((a_type_ptr)hnp->entity.ptr); } else { db_name_full(source_corresp_for_il_entry(hnp->entity.ptr, (an_il_entry_kind)hnp->entity.kind), (an_il_entry_kind)hnp->entity.kind); } /* if */ fputc('\n', f_debug); } /* if */ #endif /* DEBUG */ if (!il_entry_prefix_of(hnp->entity.ptr).keep_in_il) { if (prev_hnp == NULL) { scope->hidden_names = next_hnp; } else { prev_hnp->next = next_hnp; } /* if */ hnp->next = NULL; } else { prev_hnp = hnp; } /* if */ } /* for */ } #endif /* RECORD_HIDDEN_NAMES_IN_IL */ #if GENERATE_SOURCE_SEQUENCE_LISTS if (scope->kind == (a_scope_kind)sck_file) { eliminate_unneeded_source_sequence_entries(scope); } /* if */ #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ /* Relink the il_header.nontag_types_used_in_exception_or_rtti list to eliminate removed entities. */ prev_tp = NULL; for (tp = il_header.nontag_types_used_in_exception_or_rtti; tp != NULL; tp = next_tp) { next_tp = tp->next; if (!il_entry_prefix_of(tp).keep_in_il) { /* Remove the entry from the list by linking around it. */ if (prev_tp == NULL) { il_header.nontag_types_used_in_exception_or_rtti = tp->next; } else { prev_tp->next = tp->next; } /* if */ tp->next = NULL; } else { /* Keep the entry. */ prev_tp = tp; } /* if */ } /* for */ db_exit(); } /* eliminate_unneeded_il_entries */ #endif /* MAINTAIN_NEEDED_FLAGS */ static void remove_dynamic_initialization(a_dynamic_init_ptr dip); static void remove_expression_dynamic_initializations(an_expr_node_ptr expr) /* The indicated expression is part of an initializer. The initializer is being deleted. Unlink any dynamic initializations associated with the expression, at minimum those that have lifetimes longer than the immediately enclosing object lifetime. */ { switch (expr->kind) { case enk_object_lifetime: remove_expression_dynamic_initializations( expr->variant.object_lifetime.expr); break; case enk_lambda: case enk_temp_init: remove_dynamic_initialization(expr->variant.init.dynamic_init); break; case enk_operation: /* This covers casts, and possibly "?" and "," operators if those are ever made to pass through a temporary. */ { an_expr_node_ptr operand; for (operand = expr->variant.operation.operands; operand != NULL; operand = operand->next) { remove_expression_dynamic_initializations(operand); } /* for */ } break; default: /* No action. */ break; } /* switch */ } /* remove_expression_dynamic_initializations */ static void remove_constant_initializer_dynamic_initializations( a_constant_ptr con) /* The indicated constant is part of an initializer. The initializer is being deleted. Unlink any dynamic initializations or object lifetimes associated with the constant (e.g., if it's a nonconstant aggregate). */ { if (con->kind == (a_constant_repr_kind)ck_aggregate) { a_constant_ptr sub_con; for (sub_con = con->variant.aggregate.first_constant; sub_con != NULL; sub_con = sub_con->next) { remove_constant_initializer_dynamic_initializations(sub_con); } /* for */ } else if (con->kind == (a_constant_repr_kind)ck_dynamic_init) { remove_dynamic_initialization(con->variant.dynamic_init.ptr); } else if (con->kind == (a_constant_repr_kind)ck_init_repeat) { remove_constant_initializer_dynamic_initializations( con->variant.init_repeat.constant); } /* if */ } /* remove_constant_initializer_dynamic_initializations */ static void remove_dynamic_initialization(a_dynamic_init_ptr dip) /* Remove the indicated dynamic initialization from any initialization and destruction lists. Also remove any nested object lifetimes. */ { an_object_lifetime_ptr lifetime; lifetime = init_expr_lifetime_of(dip); if (lifetime != NULL) { /* There is a nested object lifetime. Eliminate it and everything in it. */ detach_from_object_lifetime_tree(lifetime); dip->init_expr_lifetime = NULL; /* To be neat. */ } /* if */ if (dip->kind == (a_dynamic_init_kind)dik_nonconstant_aggregate || (dip->kind == (a_dynamic_init_kind)dik_lambda && dip->variant.constant.non_constant)) { /* Remove any lifetimes on aggregate member initializers. */ remove_constant_initializer_dynamic_initializations( dip->variant.constant.ptr); } else if (dip->kind == (a_dynamic_init_kind)dik_expression || dip->kind == (a_dynamic_init_kind)dik_class_result_via_ctor) { /* Scan the sub-expression in case there's an initialization of a temporary whose lifetime was extended to the lifetime of the surrounding context. */ remove_expression_dynamic_initializations(dip->variant.expression); } /* if */ remove_from_destruction_list(dip); } /* remove_dynamic_initialization */ static void detach_constant_initializer_dyn_init_lifetimes(a_constant_ptr cp) /* Detach any lifetimes associated with the given constant from their lifetime trees. */ { if (constant_is(cp, ck_aggregate)) { a_constant_ptr sub_con; for (sub_con = cp->variant.aggregate.first_constant; sub_con != NULL; sub_con = sub_con->next) { detach_constant_initializer_dyn_init_lifetimes(sub_con); } /* for */ } else if (constant_is(cp, ck_dynamic_init)) { detach_dynamic_init_lifetimes(cp->variant.dynamic_init.ptr); } else if (constant_is(cp, ck_init_repeat)) { detach_constant_initializer_dyn_init_lifetimes( cp->variant.init_repeat.constant); } /* if */ } /* detach_constant_initializer_dyn_init_lifetimes */ static void detach_expression_dyn_init_lifetimes(an_expr_node_ptr expr) /* Detach any lifetimes associated with the given expression from their lifetime trees. */ { switch (expr->kind) { case enk_object_lifetime: detach_expression_dyn_init_lifetimes(expr->variant.object_lifetime.expr); break; case enk_lambda: case enk_temp_init: detach_dynamic_init_lifetimes(expr->variant.init.dynamic_init); break; case enk_operation: /* This covers casts, and possibly "?" and "," operators if those are ever made to pass through a temporary. */ { an_expr_node_ptr operand; for (operand = expr->variant.operation.operands; operand != NULL; operand = operand->next) { detach_expression_dyn_init_lifetimes(operand); } /* for */ } break; default: /* No action. */ break; } /* switch */ } /* detach_expression_dyn_init_lifetimes */ void detach_dynamic_init_lifetimes(a_dynamic_init_ptr dip) /* Detach any lifetimes associated with the given dynamic initialization from their lifetime trees. */ { an_object_lifetime_ptr olp; olp = init_expr_lifetime_of(dip); if (olp != NULL) { /* There exists a child lifetime for the contained expression - detach just that. */ detach_from_object_lifetime_tree(olp); } else { if (dip->kind == (a_dynamic_init_kind)dik_nonconstant_aggregate || (dip->kind == (a_dynamic_init_kind)dik_lambda && dip->variant.constant.non_constant)) { /* Detach any lifetimes on aggregate member initializers. */ detach_constant_initializer_dyn_init_lifetimes( dip->variant.constant.ptr); } else if (dip->kind == (a_dynamic_init_kind)dik_expression || dip->kind == (a_dynamic_init_kind)dik_class_result_via_ctor) { /* Scan the sub-expression in case there's an initialization of a temporary whose lifetime was extended to the lifetime of the surrounding context. */ detach_expression_dyn_init_lifetimes(dip->variant.expression); } /* if */ } /* if */ remove_from_destruction_list(dip); } /* detach_dynamic_init_lifetimes */ static void attach_constant_initializer_dyn_init_lifetimes( an_object_lifetime_ptr parent, a_constant_ptr cp) /* Attach any lifetimes associated with the given constant to the given parent lifetime. */ { if (constant_is(cp, ck_aggregate)) { a_constant_ptr sub_con; for (sub_con = cp->variant.aggregate.first_constant; sub_con != NULL; sub_con = sub_con->next) { attach_constant_initializer_dyn_init_lifetimes(parent, sub_con); } /* for */ } else if (constant_is(cp, ck_dynamic_init)) { attach_dynamic_init_lifetimes(parent, cp->variant.dynamic_init.ptr, /*only_sub_inits=*/FALSE); } else if (constant_is(cp, ck_init_repeat)) { attach_constant_initializer_dyn_init_lifetimes( parent, cp->variant.init_repeat.constant); } /* if */ } /* attach_constant_initializer_dyn_init_lifetimes */ static void attach_expression_dyn_init_lifetimes(an_object_lifetime_ptr parent, an_expr_node_ptr expr) /* Attach any lifetimes associated with the given expression to the given parent lifetime. */ { switch (expr->kind) { case enk_object_lifetime: attach_expression_dyn_init_lifetimes(parent, expr->variant.object_lifetime.expr); break; case enk_lambda: case enk_temp_init: attach_dynamic_init_lifetimes(parent, expr->variant.init.dynamic_init, /*only_sub_inits=*/FALSE); break; case enk_operation: /* This covers casts, and possibly "?" and "," operators if those are ever made to pass through a temporary. */ { an_expr_node_ptr operand; for (operand = expr->variant.operation.operands; operand != NULL; operand = operand->next) { attach_expression_dyn_init_lifetimes(parent, operand); } /* for */ } break; default: /* No action. */ break; } /* switch */ } /* attach_expression_dyn_init_lifetimes */ void attach_dynamic_init_lifetimes(an_object_lifetime_ptr parent, a_dynamic_init_ptr dip, a_boolean only_sub_inits) /* Attach any lifetimes associated with the given dynamic initializations to parent. If only_sub_inits is TRUE then the dynamic initialization itself is not attached to parent. */ { an_object_lifetime_ptr olp; olp = init_expr_lifetime_of(dip); if (olp != NULL) { /* There exists a child lifetime for the contained expression - attach just that. */ olp->parent_lifetime = parent; olp->parent_destruction_sublist = parent->destructions; olp->next = parent->child_lifetime; parent->child_lifetime = olp; } else { if (dip->kind == (a_dynamic_init_kind)dik_nonconstant_aggregate || (dip->kind == (a_dynamic_init_kind)dik_lambda && dip->variant.constant.non_constant)) { /* Attach any lifetimes on aggregate member initializers. */ attach_constant_initializer_dyn_init_lifetimes( parent, dip->variant.constant.ptr); } else if (dip->kind == (a_dynamic_init_kind)dik_expression || dip->kind == (a_dynamic_init_kind)dik_class_result_via_ctor) { /* Scan the sub-expression in case there's an initialization of a temporary whose lifetime was extended to the lifetime of the surrounding context. */ attach_expression_dyn_init_lifetimes(parent, dip->variant.expression); } /* if */ } /* if */ if (!only_sub_inits && dip->destructor != NULL) { dip->next_in_destruction_list = parent->destructions; parent->destructions = dip; dip->lifetime = parent; } /* if */ } /* attach_dynamic_init_lifetimes */ void clear_variable_definition(a_variable_ptr variable) /* Eliminate the definition of the indicated variable, if any, to turn it into a declaration instead of a definition. Among other things, this includes removing any initialization. */ { if (variable->init_kind == (an_init_kind)initk_dynamic) { /* Eliminate any destructions and object lifetimes associated with this initialization. */ a_dynamic_init_ptr dip = variable->initializer.dynamic; remove_dynamic_initialization(dip); check_assertion(!variable->source_corresp.is_local_to_function); /* Remove the dynamic initialization from the file-scope initializations list. */ { a_dynamic_init_ptr prev_dip; a_scope_ptr sp = il_header.primary_scope; a_scope_pointers_block_ptr pointers_block = &curr_translation_unit->file_scope_pointers_block; if (dip == sp->dynamic_inits) { /* The entry is first on the list. */ sp->dynamic_inits = dip->next; prev_dip = NULL; } else { /* The entry is not the first on the list. */ for (prev_dip = sp->dynamic_inits; ; prev_dip = prev_dip->next) { check_assertion_str(prev_dip != NULL, "clear_variable_definition: entry not found"); if (prev_dip->next == dip) break; } /* for */ prev_dip->next = dip->next; } /* if */ if (dip->next == NULL) { pointers_block->last_dynamic_init = prev_dip; } /* if */ dip->next = NULL; /* To be neat. */ } } /* if */ variable->init_kind = (an_init_kind)initk_none; variable->has_parenthesized_initializer = FALSE; variable->has_direct_braced_initializer = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED || GNU_EXTENSIONS_ALLOWED variable->has_flexible_array_initializer = FALSE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED || GNU_EXTENSIONS_ALLOWED */ #if GENERATE_SOURCE_SEQUENCE_LISTS eliminate_variable_definition_source_sequence_entry(variable); #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ if (variable->storage_class == (a_storage_class)sc_unspecified) { variable->storage_class = (a_storage_class)sc_extern; #if DO_IL_LOWERING variable->comdat_group = NULL; #endif /* DO_IL_LOWERING */ } /* if */ if (!variable->is_specialized) { switch_canonical_for_deleted_definition(&variable->source_corresp); } /* if */ } /* clear_variable_definition */ a_routine_ptr vtbl_decider_function_for_class(a_type_ptr class_type, a_boolean *unknown) /* Return a pointer to the routine that is the decider function for generation of the definition of the virtual function table for the given class. The routine is the first non-inline non-pure virtual function of the class. Return NULL if the class does not have such a function, or does not have one yet. Return *unknown set to TRUE (and NULL as a return value) if we can't (yet?) determine whether the class has a decider function. unknown can be NULL if we don't need that value (the return value will be NULL if the result is unknown). */ { a_routine_ptr routine; a_scope_ptr scope = class_type->variant.class_struct_union.extra_info->assoc_scope; a_boolean local_unknown; if (unknown == NULL) unknown = &local_unknown; *unknown = FALSE; if (scope_is_null_or_placeholder(scope)) { /* The class is declared but not defined. */ routine = NULL; *unknown = TRUE; } else if (!class_type->variant.class_struct_union.any_virtual_functions) { /* The class has no virtual functions. */ routine = NULL; } else { /* The class is defined. Look at the member functions. */ for (routine = scope->routines; routine != NULL; routine = routine->next) { /* The first non-inline virtual non-pure member function in the class is the decider function. In the IA-64 ABI, the function has to be declared inline within the class definition. */ if (routine->is_virtual && !routine->pure_virtual #if IA64_ABI && DO_IL_LOWERING /* Ignore alternate entry points for constructors and destructors. */ && routine->primary_ctor_or_dtor == NULL #endif /* IA64_ABI && DO_IL_LOWERING */ ) { if (!routine->is_template_function) { /* A non-template. It's the decider if it's not inline. */ if ( #if IA64_ABI && DO_IL_LOWERING !targ_ia64_abi_variant_key_function ? !routine->inline_in_class_definition : !routine->is_inline #else /* !(IA64_ABI && DO_IL_LOWERING) */ !routine->is_inline #endif /* IA64_ABI && DO_IL_LOWERING */ ) break; } else { /* A member function of a template class is not marked as inline until it is fully instantiated, so we have to call a function to see whether it is really inline. */ if (!rout_is_inline_template_function(routine, #if IA64_ABI && DO_IL_LOWERING !targ_ia64_abi_variant_key_function #else /* !(IA64_ABI && DO_IL_LOWERING) */ /*in_class=*/FALSE #endif /* IA64_ABI && DO_IL_LOWERING */ )) break; #if BACK_END_IS_CP_GEN_BE && \ (NONCLASS_TEMPLATE_INSTANTIATIONS_IN_SOURCE_SEQUENCE_LISTS || \ PROTOTYPE_INSTANTIATIONS_IN_IL) /* The routine is inline, so ordinarily it is not a decider. However, with the C++-generating back end there are some special cases. */ /* In SSI versions, we move the definitions for instantiations out of the class body, so we might be changing whether they are decider functions. */ /* In PI-in-IL versions, we put out uninstantiated inline members as non-inline in some configurations, so we want to consider them potential decider functions. */ *unknown = TRUE; break; #endif /* BACK_END_IS_CP_GEN_BE && ... */ } /* if */ } /* if */ } /* for */ } /* if */ if (*unknown) routine = NULL; if (routine != NULL) { routine->considered_decider_function_at_some_point = TRUE; } /* if */ return routine; } /* vtbl_decider_function_for_class */ #endif /* !STANDALONE_UTILITY_PROGRAM */ #if MICROSOFT_EXTENSIONS_ALLOWED an_assembly_visibility get_assembly_visibility_of(a_type_ptr type) /* Return the top-level C++/CLI visibility of the given type (if any). */ { an_assembly_visibility result; if (is_immediate_class_type(type)) { result = enum_cast( class_type_supp(type)->assembly_visibility); } else if (type->kind == (a_type_kind)tk_integer) { result = enum_cast( integer_type_supp(type)->assembly_visibility); } else { result = av_none; } /* if */ return result; } /* an_assembly_visibility */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if ONE_INSTANTIATION_PER_OBJECT unsigned long assign_instantiation_needed_bit_number(void) /* Increment the number of external template entities and return the bit number to be used for the next instantiation needed bit number. */ { il_header.number_of_external_nonclass_template_entities++; /* When generating one instantiation per object, assign a "bit number" to each external nonclass entity. Actually, two bits: the second is used for class definition_needed bits. Bit numbers 1 and 2 are reserved for the needed and definition_needed flags for entities in the compilation that are not instantiations. */ return (il_header.number_of_external_nonclass_template_entities*2) + 1; } /* assign_instantiation_needed_bit_number */ void clear_instantiation_needed_flags_scan_state( an_instantiation_needed_flags_scan_state_ptr infssp, a_source_correspondence *scp) /* Initialize an_instantiation_needed_flags_scan_state to begin scanning for per-instantiation "needed" flags in the set associated with scp. */ { infssp->curr_segment = scp->per_instantiation_needed_flags; infssp->first_bit_this_segment = 1; infssp->byte_number = 0; infssp->bit_number = -1; } /* clear_instantiation_needed_flags_scan_state */ unsigned long next_set_instantiation_needed_flag( an_instantiation_needed_flags_scan_state_ptr infssp) /* Return the bit number of the next set per-instantiation "needed" flag in a bit vector. infssp indicates the current state of the scan, and is updated on return. Return 0 if there is no next set bit. Note that "definition needed" bits are returned as well; the caller must ignore them if appropriate. */ { unsigned long next_set_bit; a_per_instantiation_needed_flags_entry_ptr curr_segment = infssp->curr_segment; unsigned long first_bit_this_segment = infssp->first_bit_this_segment; int byte_number = infssp->byte_number; int bit_number = infssp->bit_number; for (; curr_segment != NULL; curr_segment = curr_segment->next, first_bit_this_segment += BYTES_PER_INSTANTIATION_NEEDED_FLAG_ENTRY*CHAR_BIT) { a_byte byte = curr_segment->bytes[byte_number]; /* Clear the bits that have already been looked at, in a copy of the current byte. */ if (bit_number >= 0) { /* Current byte has been started. bit_number indicates the bit last examined. */ if (bit_number == CHAR_BIT-1) { /* Nothing left in this byte. */ byte = 0; } else { /* Mask off the bits that have been looked at already. */ byte &= (a_byte)((~(a_byte)0) << (bit_number+1)); } /* if */ } /* if */ /* Advance over bytes with no bits set. The first time around, this tests the byte produced above, which has the bits that have already been examined turned off. */ while (byte == 0) { bit_number = -1; if (byte_number == BYTES_PER_INSTANTIATION_NEEDED_FLAG_ENTRY-1) { byte_number = 0; goto next_segment; } /* if */ byte_number++; byte = curr_segment->bytes[byte_number]; } /* while */ /* Here, we have a byte with some bits set. Find the first of those. */ for (;;) { bit_number++; if (byte & (((a_byte)1) << bit_number)) { /* Found the first set bit. */ next_set_bit = first_bit_this_segment; next_set_bit += (unsigned)byte_number * CHAR_BIT; next_set_bit += (unsigned)bit_number; goto end_of_routine; } /* if */ } /* if */ next_segment:; } /* for */ /* Fell off end of list. */ next_set_bit = 0; end_of_routine: infssp->curr_segment = curr_segment; infssp->first_bit_this_segment = first_bit_this_segment; infssp->byte_number = byte_number; infssp->bit_number = bit_number; return next_set_bit; } /* next_set_instantiation_needed_flag */ a_boolean instantiation_needed_flag_is_set(a_source_correspondence *scp, int bit_offset) /* Fetch and return the value of the per-instantiation "needed" flag associated with source correspondence entry *scp and with the bit number given by global variable needed_flag_bit_number plus bit_offset. */ { a_boolean flag_value; a_per_instantiation_needed_flags_entry_ptr ptr; unsigned long first_bit_this_segment; unsigned long byte_number; unsigned long bit_number = needed_flag_bit_number + (unsigned)bit_offset; #define BITS_PER_ENTRY (BYTES_PER_INSTANTIATION_NEEDED_FLAG_ENTRY*CHAR_BIT) /* Loop through the list of entries to find the one containing the bit we want to test. */ for (ptr = scp->per_instantiation_needed_flags, first_bit_this_segment = 1; ptr != NULL && (first_bit_this_segment + BITS_PER_ENTRY) <= bit_number; ptr = ptr->next, first_bit_this_segment += BITS_PER_ENTRY) { } /* for */ if (ptr == NULL) { /* Ran off the end of the list. All bits out here are presumed to be zero. */ flag_value = FALSE; } else { bit_number -= first_bit_this_segment; byte_number = bit_number / CHAR_BIT; bit_number = bit_number % CHAR_BIT; flag_value = (ptr->bytes[byte_number] >> bit_number) & 1; } /* if */ return flag_value; #undef BITS_PER_ENTRY } /* instantiation_needed_flag_is_set */ #if !STANDALONE_UTILITY_PROGRAM void set_instantiation_needed_flag(a_source_correspondence *scp, int bit_offset, int new_value) /* Set to new_value the per-instantiation "needed" flag associated with source correspondence entry *scp and with the bit number given by global variable needed_flag_bit_number plus bit_offset. */ { a_per_instantiation_needed_flags_entry_ptr ptr, prev_ptr; unsigned long first_bit_this_segment; unsigned long byte_number; unsigned long bit_number = needed_flag_bit_number + (unsigned)bit_offset; a_byte bit; #define BITS_PER_ENTRY (BYTES_PER_INSTANTIATION_NEEDED_FLAG_ENTRY*CHAR_BIT) if (in_secondary_trans_unit(scp)) { /* This should only come up with errors (there remain some pointers from the primary IL to the secondary IL because the copy process was suppressed due to errors). When it does, ignore the call. */ check_assertion(is_at_least_one_error()); } else { /* Loop through the list of entries to find the one containing the bit we want to test. */ for (prev_ptr = NULL, ptr = scp->per_instantiation_needed_flags, first_bit_this_segment = 1; ; prev_ptr = ptr, ptr = ptr->next, first_bit_this_segment += BITS_PER_ENTRY) { if (ptr == NULL) { /* Ran off the end of the list, so allocate another entry. */ ptr = alloc_per_instantiation_needed_flags_entry(in_file_scope(scp)); if (prev_ptr == NULL) { scp->per_instantiation_needed_flags = ptr; } else { prev_ptr->next = ptr; } /* if */ } /* if */ if ((first_bit_this_segment + BITS_PER_ENTRY) > bit_number) { /* This segment contains the bit we want. */ break; } /* if */ } /* for */ bit_number -= first_bit_this_segment; byte_number = bit_number / CHAR_BIT; bit_number = bit_number % CHAR_BIT; bit = (a_byte)(1 << bit_number); if (new_value != 0) { /* Set the bit. */ ptr->bytes[byte_number] |= bit; } else { /* Clear the bit. */ ptr->bytes[byte_number] &= ~bit; } /* if */ #undef BITS_PER_ENTRY if (needed_flag_bit_number != 1 && new_value != 0 && (scp->name_linkage == (a_name_linkage_kind)nlk_internal || /* When lowering extern inline, a function can be static, then external, then static again. Make sure it gets marked. */ scp->externalized)) { /* This is a static entity that is needed from an instantiation. Mark the entity so that it will be made external so it can be accessed from the instantiation file. */ scp->static_used_by_instantiation = TRUE; } /* if */ } /* if */ } /* set_instantiation_needed_flag */ #endif /* !STANDALONE_UTILITY_PROGRAM */ #endif /* ONE_INSTANTIATION_PER_OBJECT */ a_namespace_ptr f_skip_namespace_aliases(a_namespace_ptr nsp) /* nsp points to either a namespace or a namespace alias. If nsp is a namespace, the pointer to that namespace is returned. If nsp is a namespace alias, a pointer to the real namespace is returned. */ { while (nsp->is_namespace_alias) { nsp = nsp->variant.assoc_namespace; check_assertion_str2(nsp != NULL, "f_skip_namespace_aliases:", "NULL namespace pointer"); } /* while */ return nsp; } /* f_skip_namespace_aliases */ #if !STANDALONE_UTILITY_PROGRAM a_boolean intf_rout_is_inline_template_function(a_routine_ptr rout) /* Interface routine to rout_is_inline_template_function in templates.c. Exists to avoid difficulties with referring to a function in templates.c from a macro (rout_is_inline) in il.h. */ { return in_front_end && rout_is_inline_template_function(rout, /*in_class=*/FALSE); } /* intf_rout_is_inline_template_function */ a_boolean is_member_of_unnamed_namespace(a_source_correspondence *scp) /* Return TRUE if the entity with the specified source-correspondence is a direct or indirect member of an unnamed namespace. */ { a_boolean found = FALSE; a_namespace_ptr nsp; if (scp->is_class_member) { scp = &scp_parent_class(scp)->source_corresp; found = is_member_of_unnamed_namespace(scp); } else if ((nsp = scp_parent_namespace_or_null(scp)) != NULL) { if (unmangled_name_of(&nsp->source_corresp) == NULL) { found = TRUE; } else { found = is_member_of_unnamed_namespace(&nsp->source_corresp); } /* if */ } else if (scp->is_local_to_function && scp->enclosing_routine != NULL) { found = is_member_of_unnamed_namespace( &scp->enclosing_routine->source_corresp); } /* if */ return found; } /* is_member_of_unnamed_namespace */ #if DO_IL_LOWERING a_boolean routine_should_be_externalized_for_exported_templates( a_routine_ptr rout) /* Return TRUE if the indicated routine should be externalized because it might be referenced from an exported template. */ { a_boolean should_externalize = (any_exported_templates() && rout->storage_class == (a_storage_class)sc_static #if ONE_INSTANTIATION_PER_OBJECT #if DUPLICATE_SPECIAL_STATICS_IN_INSTANTIATION_SLICES && !rout->source_corresp.duplicate_static_in_instantiation_slices #endif /* DUPLICATE_SPECIAL_STATICS_IN_INSTANTIATION_SLICES */ #endif /* ONE_INSTANTIATION_PER_OBJECT */ ); return should_externalize; } /* routine_should_be_externalized_for_exported_templates */ a_boolean variable_should_be_externalized_for_exported_templates( a_variable_ptr var) /* Return TRUE if the indicated variable should be externalized because it might be referenced from an exported template. */ { a_boolean should_externalize = (any_exported_templates() && var->storage_class == (a_storage_class)sc_static #if ONE_INSTANTIATION_PER_OBJECT #if DUPLICATE_SPECIAL_STATICS_IN_INSTANTIATION_SLICES && !var->source_corresp.duplicate_static_in_instantiation_slices #endif /* DUPLICATE_SPECIAL_STATICS_IN_INSTANTIATION_SLICES */ #endif /* ONE_INSTANTIATION_PER_OBJECT */ ); return should_externalize; } /* variable_should_be_externalized_for_exported_templates */ #endif /* DO_IL_LOWERING */ #if DO_IL_LOWERING || NEED_NAME_MANGLING static a_boolean is_or_will_be_extern_inline(a_routine_ptr routine) /* Return TRUE if the indicated function is or will be extern inline. It might become extern inline because it's potentially referenced from an exported template. See externalize_statics_for_exported_templates. */ { a_boolean is_extern_inline = (routine->is_inline && (treat_as_extern_inline(routine) #if DO_IL_LOWERING || routine_should_be_externalized_for_exported_templates(routine) #endif /* DO_IL_LOWERING */ )); return is_extern_inline; } /* is_or_will_be_extern_inline */ a_boolean routine_might_exist_in_multiple_copies(a_routine_ptr rout) /* Return TRUE if the indicated routine might exist in multiple copies at link time or run time. For example, it might be an extern inline routine that is expanded in more than one translation unit, or a template that is instantiated in more than one translation unit. */ { a_boolean multiple_copies = FALSE; /* For member functions of local classes, move out to the ultimate enclosing function. */ while (rout->source_corresp.is_local_to_function) { a_routine_ptr enclosing_rout = NULL; if (rout->source_corresp.is_class_member) { enclosing_rout = enclosing_routine_for_local_type_or_null( parent_class_of(rout)); } /* if */ if (enclosing_rout != NULL) { rout = enclosing_rout; } else { /* This should only happen with types defined in local function prototype scopes, and such definitions elicit errors in C++. (In error recovery mode the result of this function does not really matter.) */ expect_error(); break; } /* if */ } /* while */ if (!C_mode() && is_or_will_be_extern_inline(rout)) { /* An extern inline routine might be expanded in more than one translation unit. This might be true even if extern inline routines are instantiated. */ multiple_copies = TRUE; } else if (rout->source_corresp.name_linkage == (a_name_linkage_kind)nlk_internal) { /* A routine with internal linkage cannot exist in multiple translation units. */ multiple_copies = FALSE; #if LINKER_CAN_DISCARD_DUPLICATE_DEFINITIONS } else if (rout->is_template_function && !rout->is_specialized) { /* A template instance, in a mode where the linker can merge multiple definitions (in other words, where we have COMDATs). This handling is required for the strict IA-64 ABI, but it's probably also a good idea in any environment where COMDATs are used, because we assume that where users *can* generate multiple instances and get no linker error, they *will* generate such instances, and the merging had better work right. */ multiple_copies = TRUE; #endif /* LINKER_CAN_DISCARD_DUPLICATE_DEFINITIONS */ #if ABI_CHANGES_FOR_COVARIANT_VIRTUAL_FUNC_RETURN && DO_IL_LOWERING } else if (rout->covariant_return_virtual_override || rout->overriding_function_for_wrapper != NULL) { /* For a covariant overriding virtual function and its wrapper routines, promote the local statics in case the implementation technique is to replicate the body of the primary function. */ multiple_copies = TRUE; } else if (rout->next != NULL && rout->next->overriding_function_for_wrapper == rout) { /* Also, if the routine has a thunk the thunk might be implemented by replicating the function body. */ multiple_copies = TRUE; #endif /* ABI_CHANGES_FOR_COVARIANT_VIRTUAL_FUNC_RETURN && DO_IL_LOWERING */ } /* if */ return multiple_copies; } /* routine_might_exist_in_multiple_copies */ #endif /* DO_IL_LOWERING || NEED_NAME_MANGLING */ a_param_type_ptr get_routine_param_types(a_routine_ptr rp) /* Given a routine pointer, return the list of parameter types. */ { a_type_ptr rtp = skip_typerefs(rp->type); a_routine_type_supplement_ptr rtp_sup = rout_type_supp(rtp); return rtp_sup->param_type_list; } /* get_routine_param_types */ #if DEBUG unsigned long show_il_space_used(void) /* Display and return the amount of space used for various IL tables. */ { unsigned long grand_total = 0; grand_total = show_il_alloc_space_used(grand_total); (void)fputc('\n', f_debug); db_space_used_other("get_based_type_calls", num_get_based_type_calls, ""); (void)fputc('\n', f_debug); db_space_used_other("num_shareable_constants", num_shareable_constants, ""); db_space_used_other("Percent of buckets used", (100 * num_used_shareable_constant_buckets) / SIZE_SHAREABLE_CONSTANTS_TABLE, ""); if (num_used_shareable_constant_buckets != 0) { db_space_used_float_other("Avg non-empty bucket len", (double)num_shareable_constants / (double)num_used_shareable_constant_buckets, ""); } /* if */ db_space_used_other("num func shareable consts", num_func_shareable_constants, ""); db_space_used_other("Number of searches", num_searches_for_shareable_constants, ""); if (num_searches_for_shareable_constants != 0) { db_space_used_float_other("Avg compares/search", (double)num_compares_for_shareable_constants / (double)num_searches_for_shareable_constants, ""); } /* if */ (void)fputc('\n', f_debug); db_space_used_general_buffer("temp text buffer", (unsigned long)size_temp_text_buffer); return grand_total; } /* show_il_space_used */ #endif /* DEBUG */ #if UPC_EXTENSIONS_ALLOWED a_boolean upc_block_size_too_large(a_host_large_unsigned block_size) /* If the block size is too large, issue an error and return TRUE; otherwise, return FALSE. */ { a_boolean result = FALSE; if (block_size > (a_host_large_unsigned)max_upc_block_size) /*lint !e571*/ { a_number_buffer size_buf(max_upc_block_size); error(ec_shared_block_size_too_large, size_buf.as_temp_characters()); result = TRUE; } /* if */ return result; } /* upc_block_size_too_large */ #endif /* UPC_EXTENSIONS_ALLOWED */ a_type_ptr init_predeclared_class(a_type_kind kind, a_const_char *name) /* Create a type entry for a predeclared class of the specified kind. It is given the name indicated and a symbol is created, but the symbol is not entered into the symbol table. */ { a_type_ptr predeclared_type; predeclared_type = alloc_type(kind); if (!C_mode()) { /* Default name-linkage for classes is C++ external linkage. */ predeclared_type->source_corresp.name_linkage = (a_name_linkage_kind)nlk_cplusplus_external; } /* if */ make_symbol_for_predeclared_type(predeclared_type, name); return predeclared_type; } /* init_predeclared_class */ void enter_predeclared_class(a_type_ptr predeclared_type, a_scope_depth scope_depth, a_source_position *pos) /* Enter the given predeclared type (e.g., type_info) at the given scope depth with the given source position. */ { a_symbol_ptr sym = symbol_for(predeclared_type); a_namespace_ptr nsp = NULL; switch (scope_stack[scope_depth].kind) { case sck_file: nsp = NULL; break; case sck_namespace: case sck_namespace_extension: nsp = scope_stack[scope_depth].il_scope->variant.assoc_namespace; break; default: unexpected_condition(); } /* switch */ sym->decl_position = *pos; reenter_symbol(sym, scope_depth, /*suppress_error=*/FALSE); /* Call set_source_corresp again to get everything in sync. */ set_source_corresp(&(predeclared_type->source_corresp), sym); set_namespace_membership(sym, &(predeclared_type->source_corresp), nsp); /* The referenced flag may have been reset by set_source_corresp. */ predeclared_type->source_corresp.referenced = sym->referenced; add_to_types_list(predeclared_type, scope_depth); } /* enter_predeclared_class */ a_targ_alignment alignment_of_variable(a_variable_ptr vp) /* Return the alignment of the given variable. In GNU and Microsoft modes, this may be affected by attributes specified on the variable declaration. In some rare cases (e.g., in GNU 3.0 compatibility mode), the result may be different from that implied by the alignof operator applied to the variable (see scan_alignof_operator for details). */ { a_targ_alignment result; if (vp->alignment != 0) { /* Alignment was specified explicitly on the variable declaration. */ result = vp->alignment; } else { result = alignment_of_type(vp->type); } /* if */ return result; } /* alignment_of_variable */ #endif /* !STANDALONE_UTILITY_PROGRAM */ an_attribute_ptr find_attribute(an_attribute_kind kind, an_attribute_ptr attributes) /* Return the first attribute of the given kind in the given list of attributes, or NULL if there is no such item. */ { an_attribute_ptr ap; for (ap = attributes; ap != NULL; ap = ap->next) { if (ap->kind == kind) break; } /* for */ return ap; } /* find_attribute */ #if GNU_FUNCTION_MULTIVERSIONING an_attribute_ptr find_last_target_attribute(an_attribute_ptr attributes) /* Return the last "target" attribute in the given list of attributes; this seems to match the GNU behavior (it only acts on the last "target" attribute in a set). As this call is typically made before attribute checking has occurred, only consider string attributes (diagnostics will be issued when the attributes are later processed for the non-string cases). */ { an_attribute_ptr ap, last = NULL; for (ap = attributes; ap != NULL; ap = ap->next) { if (ap->kind == ak_target) { an_attribute_arg_ptr aap = ap->arguments; if (aap->kind == (an_attribute_arg_kind)aak_raw_token && aap->variant.token[0] == '"') { last = ap; } /* if */ } /* if */ } /* for */ return last; } /* find_last_target_attribute */ #endif /* GNU_FUNCTION_MULTIVERSIONING */ /* The following table defines the value category expected for the operands of each operation node. lvalues and xvalues are always treated the same for the purposes of this test, so the only categories represented are glvalues and prvalues. */ /* Definition of the bits in lvalue_rvalue_test. */ #define LVRV_NO_REQUIREMENTS 0x0 #define LVRV_OPND1_IS_PRVALUE 0x1 #define LVRV_OPND1_IS_GLVALUE 0x2 #define LVRV_OPND1_IS_GLVALUE_IF_EXPR_IS 0x4 #define LVRV_OPND2_IS_PRVALUE 0x8 #define LVRV_OPND2_IS_GLVALUE 0x10 #define LVRV_OPND2_IS_GLVALUE_IF_EXPR_IS 0x20 #define LVRV_VA_LIST_OPERATION 0x40 #define LVRV_DISTINGUISHED_VALUE_FOR_LAST 0xfd CONSTINIT_ARRAY(static, a_byte, lvalue_rvalue_test, eok_last + 1) = { /* eok_address_of: */ LVRV_OPND1_IS_GLVALUE, /* eok_reference_to: */ LVRV_NO_REQUIREMENTS, /* eok_handle_to: */ LVRV_NO_REQUIREMENTS, /* eok_indirect: */ LVRV_OPND1_IS_PRVALUE, /* eok_ref_indirect: */ LVRV_OPND1_IS_PRVALUE, /* eok_cast: */ LVRV_OPND1_IS_PRVALUE, /* eok_lvalue_cast: */ LVRV_OPND1_IS_GLVALUE, /* eok_ref_cast: */ LVRV_OPND1_IS_GLVALUE, /* eok_lvalue_adjust: */ LVRV_OPND1_IS_GLVALUE, /* eok_class_rvalue_adjust: */ LVRV_OPND1_IS_PRVALUE, /* eok_box: */ LVRV_OPND1_IS_PRVALUE, /* eok_handle_to_box: */ LVRV_OPND1_IS_PRVALUE, /* eok_unbox: */ LVRV_OPND1_IS_PRVALUE, /* eok_unbox_lvalue: */ LVRV_OPND1_IS_GLVALUE, /* eok_base_class_cast: */ LVRV_OPND1_IS_GLVALUE_IF_EXPR_IS, /* eok_derived_class_cast: */ LVRV_OPND1_IS_GLVALUE_IF_EXPR_IS, /* eok_pm_base_class_cast: */ LVRV_OPND1_IS_PRVALUE, /* eok_pm_derived_class_cast: */ LVRV_OPND1_IS_PRVALUE, /* eok_dynamic_cast: */ LVRV_OPND1_IS_PRVALUE, /* eok_ref_dynamic_cast: */ LVRV_OPND1_IS_GLVALUE, /* eok_bool_cast: */ LVRV_OPND1_IS_PRVALUE, /* eok_array_to_pointer: */ LVRV_NO_REQUIREMENTS, /* eok_dot_vacuous_destructor_call: */ LVRV_NO_REQUIREMENTS, /* eok_points_to_vacuous_destructor_call: */ LVRV_OPND1_IS_PRVALUE, #if MICROSOFT_EXTENSIONS_ALLOWED /* eok_assume: */ LVRV_OPND1_IS_PRVALUE, #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* eok_noexcept: */ LVRV_NO_REQUIREMENTS, /* eok_parens: */ LVRV_NO_REQUIREMENTS, /* eok_negate: */ LVRV_OPND1_IS_PRVALUE, /* eok_unary_plus: */ LVRV_OPND1_IS_PRVALUE, /* eok_complement: */ LVRV_OPND1_IS_PRVALUE, /* eok_not: */ LVRV_OPND1_IS_PRVALUE, /* eok_vector_not: */ LVRV_OPND1_IS_PRVALUE, /* eok_vector_fill: */ LVRV_OPND1_IS_PRVALUE, #if C99_IL_EXTENSIONS_SUPPORTED /* eok_xconj: */ LVRV_OPND1_IS_PRVALUE, /* eok_real_part: */ LVRV_NO_REQUIREMENTS, /* eok_imag_part: */ LVRV_NO_REQUIREMENTS, #endif /* C99_IL_EXTENSIONS_SUPPORTED */ /* eok_post_incr: */ LVRV_OPND1_IS_GLVALUE, /* eok_post_decr: */ LVRV_OPND1_IS_GLVALUE, /* eok_pre_incr: */ LVRV_OPND1_IS_GLVALUE, /* eok_pre_decr: */ LVRV_OPND1_IS_GLVALUE, /* eok_add: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_subtract: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_multiply: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_divide: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_remainder: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, #if C99_IL_EXTENSIONS_SUPPORTED /* eok_jmultiply: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_jdivide: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_fjadd: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_jfadd: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_fjsubtract: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_jfsubtract: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, #endif /* C99_IL_EXTENSIONS_SUPPORTED */ /* eok_padd: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_psubtract: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_pdiff: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_shiftl: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_shiftr: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_and: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_or: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_xor: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_eq: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_ne: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_gt: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_lt: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_ge: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_le: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_spaceship: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_vector_eq: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_vector_ne: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_vector_gt: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_vector_lt: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_vector_ge: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_vector_le: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_gnu_min: */ LVRV_NO_REQUIREMENTS, /* eok_gnu_max: */ LVRV_NO_REQUIREMENTS, /* eok_assign: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_add_assign: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_subtract_assign: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_multiply_assign: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_divide_assign: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_remainder_assign: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_shiftl_assign: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_shiftr_assign: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_and_assign: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_or_assign: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_xor_assign: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_padd_assign: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_psubtract_assign: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_bassign: */ LVRV_OPND1_IS_GLVALUE, /* eok_land: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_lor: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_vector_land: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_vector_lor: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_comma: */ LVRV_NO_REQUIREMENTS, /* eok_subscript: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_vector_subscript: */ LVRV_OPND2_IS_PRVALUE, /* eok_dot_field: */ LVRV_NO_REQUIREMENTS, /* eok_points_to_field: */ LVRV_OPND1_IS_PRVALUE, /* eok_pm_field: */ LVRV_OPND2_IS_PRVALUE, /* eok_pm_points_to_field: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_dot_pm_func_ptr */ LVRV_OPND2_IS_PRVALUE, /* eok_points_to_pm_func_ptr */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_dot_static: */ LVRV_NO_REQUIREMENTS, /* eok_points_to_static: */ LVRV_OPND1_IS_PRVALUE, /* eok_virtual_function_ptr: */ LVRV_OPND1_IS_PRVALUE, /* eok_question: */ LVRV_OPND1_IS_PRVALUE, /* eok_vector_question: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_GLVALUE_IF_EXPR_IS, /* eok_call: */ LVRV_OPND1_IS_PRVALUE, /* eok_dot_member_call: */ LVRV_OPND1_IS_PRVALUE, /* eok_points_to_member_call: */ LVRV_OPND1_IS_PRVALUE, /* eok_dot_pm_call: */ LVRV_OPND1_IS_PRVALUE, /* eok_points_to_pm_call: */ LVRV_OPND1_IS_PRVALUE, /* eok_cli_subscript: */ LVRV_OPND1_IS_PRVALUE | LVRV_OPND2_IS_PRVALUE, /* eok_va_start: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_GLVALUE | LVRV_VA_LIST_OPERATION, /* eok_va_arg: */ LVRV_OPND1_IS_GLVALUE | LVRV_VA_LIST_OPERATION, /* eok_va_end: */ LVRV_OPND1_IS_GLVALUE | LVRV_VA_LIST_OPERATION, /* eok_va_copy: */ LVRV_OPND1_IS_GLVALUE | LVRV_OPND2_IS_GLVALUE | LVRV_VA_LIST_OPERATION, /* eok_va_start_single_operand: */ LVRV_OPND1_IS_GLVALUE | LVRV_VA_LIST_OPERATION, /* eok_lvalue: */ LVRV_OPND1_IS_PRVALUE, /* eok_await: */ LVRV_NO_REQUIREMENTS, /* eok_yield: */ LVRV_NO_REQUIREMENTS, /* eok_splice: */ LVRV_NO_REQUIREMENTS, /* eok_error: */ LVRV_NO_REQUIREMENTS, /* eok_last: */ LVRV_DISTINGUISHED_VALUE_FOR_LAST } CONSTINIT_ARRAY_END(lvalue_rvalue_test) a_boolean operator_takes_lvalue_op1(an_expr_operator_kind op) /* Return TRUE if the given expression operator takes an lvalue as its first operand. That includes some operators that will also accept an xvalue as the first operand, but for which the primary use is for lvalues. */ { return (lvalue_rvalue_test[(int)op] & LVRV_OPND1_IS_GLVALUE) != 0; } /* operator_takes_lvalue_op1 */ a_boolean operator_takes_lvalue_op2(an_expr_operator_kind op) /* Return TRUE if the given expression operator takes an lvalue as its second operand. That includes some operators that will also accept an xvalue as the second operand, but for which the primary use is for lvalues. */ { return (lvalue_rvalue_test[(int)op] & LVRV_OPND2_IS_GLVALUE) != 0; } /* operator_takes_lvalue_op2 */ #if CHECKING #if !STANDALONE_UTILITY_PROGRAM static a_boolean node_operands_have_correct_value_category(an_expr_node_ptr node) /* If node is an operation expression node, return FALSE if any of its operands have the is_lvalue/is_xvalue flags set incorrectly; return TRUE otherwise. */ { a_boolean operand_error = FALSE; if (is_operation_node(node)) { an_expr_operator_kind op = node->variant.operation.kind; a_byte flags = lvalue_rvalue_test[(int)op]; an_expr_node_ptr operand_1 = node->variant.operation.operands; an_expr_node_ptr operand_2 = operand_1->next; if (flags & LVRV_VA_LIST_OPERATION) { /* The builtin operations for stdarg support usually take va_list lvalues, but if va_list is an array type they take rvalue pointer operands. */ if ((flags & LVRV_OPND1_IS_GLVALUE) && !is_glvalue_node(operand_1) && is_pointer_type(operand_1->type)) { flags ^= LVRV_OPND1_IS_GLVALUE; flags |= LVRV_OPND1_IS_PRVALUE; } /* if */ if ((flags & LVRV_OPND2_IS_GLVALUE) && !is_glvalue_node(operand_2) && is_pointer_type(operand_2->type)) { flags ^= LVRV_OPND2_IS_GLVALUE; flags |= LVRV_OPND2_IS_PRVALUE; } /* if */ if (gnu_mode && node_operator_is(node, eok_va_start)) { /* In GNU mode, the second operand of __builtin_va_start is ignored. */ flags ^= LVRV_OPND2_IS_GLVALUE; } /* if */ } /* if */ if (flags & LVRV_OPND1_IS_PRVALUE) { /* The first operand is supposed to be a prvalue. */ if (is_glvalue_node(operand_1)) { if (!il_header.il_has_C_semantics && node_operator_is(node, eok_cast) && (is_void_type(node->type) || expr_is_instantiation_dependent(operand_1))) { /* In C++, a cast to void can have a glvalue operand. Furthermore, the value category of an instantiation-dependent expression is unreliable. */ } else { operand_error = TRUE; } /* if */ } /* if */ } else if (flags & LVRV_OPND1_IS_GLVALUE) { /* The first operand is supposed to be a glvalue. */ if (!is_glvalue_node(operand_1)) operand_error = TRUE; } else if (flags & LVRV_OPND1_IS_GLVALUE_IF_EXPR_IS) { /* The first operand is supposed to be a glvalue if and only if the expression returns a glvalue. This is complicated, however, by the fact that the expression may have an implied glvalue-to-prvalue conversion. */ a_boolean eff_node_is_glvalue = is_glvalue_node(node); if (node_includes_glvalue_to_prvalue_conv(node)) { eff_node_is_glvalue = TRUE; } /* if */ if (eff_node_is_glvalue != is_glvalue_node(operand_1)) { operand_error = TRUE; } /* if */ } /* if */ if (flags & LVRV_OPND2_IS_PRVALUE) { /* The second operand is supposed to be a prvalue. */ if (is_glvalue_node(operand_2)) operand_error = TRUE; } else if (flags & LVRV_OPND2_IS_GLVALUE) { /* The second operand is supposed to be a glvalue. */ if (!is_glvalue_node(operand_2)) { /* Probably an error, but check for one special case. */ if (gpp_mode && op == (an_expr_operator_kind)eok_va_start && is_variable_node(operand_2) && is_lvalue_reference_type(operand_2->type)) { /* g++ allows use of va_start with a parameter of lvalue reference type. This situation is represented in the IL as an rvalue variable designating the parameter and is not an error. */ } else { operand_error = TRUE; } /* if */ } /* if */ } else if (flags & LVRV_OPND2_IS_GLVALUE_IF_EXPR_IS) { /* The second operand is supposed to be a glvalue if and only if the expression returns a glvalue. This is complicated, however, by the fact that the expression may have an implied glvalue-to-prvalue conversion. */ a_boolean eff_node_is_glvalue = is_glvalue_node(node); if (node_includes_glvalue_to_prvalue_conv(node)) { eff_node_is_glvalue = TRUE; } /* if */ if (eff_node_is_glvalue != is_glvalue_node(operand_2)) { if (is_glvalue_node(node) && op == (an_expr_operator_kind)eok_question && skip_parens(operand_2)->kind == (an_expr_node_kind)enk_throw) { /* It's okay for a glvalue eok_question node to have a prvalue enk_throw operand. */ } else { operand_error = TRUE; } /* if */ } else if (op == (an_expr_operator_kind)eok_question && eff_node_is_glvalue != is_glvalue_node(operand_2->next)) { /* A glvalue result for ?: also requires that the third operand be a glvalue. */ if (is_glvalue_node(node) && op == (an_expr_operator_kind)eok_question && skip_parens(operand_2->next)->kind == (an_expr_node_kind)enk_throw) { /* It's okay for a glvalue eok_question node to have a prvalue enk_throw operand. */ } else { operand_error = TRUE; } /* if */ } /* if */ } /* if */ #if PROTOTYPE_INSTANTIATIONS_IN_IL if (operand_error && (is_template_dependent_type(node->type) || is_template_dependent_type(operand_1->type) || (operand_2 != NULL && is_template_dependent_type(operand_2->type)) || (operand_2 != NULL && operand_2->next != NULL && is_template_dependent_type(operand_2->next->type)))) { /* Generic operands are not always value-category-correct. */ operand_error = FALSE; } /* if */ #endif /* PROTOTYPE_INSTANTIATIONS_IN_IL */ } /* if */ #if CHECKING if (node->orig_lvalue_type != NULL) { /* If an original lvalue type is recorded, the node must have been "rvalued" (i.e., converted from a glvalue to a prvalue by just clearing the is_lvalue and is_xvalue flags). The "rvaluing" may not take effect if the result is not used or of type void. Also, don't attempt to look into GNU statement expressions. */ check_assertion(node_includes_glvalue_to_prvalue_conv(node) || (is_operation_node(node) && node->variant.operation.is_reference_cast) || node->result_is_not_used || node_is(node, enk_statement) || is_void_type(node->type)); } /* if */ #endif /* CHECKING */ return !operand_error; } /* node_operands_have_correct_value_category */ static void check_node_operand_lvalueness( an_expr_node_ptr node, an_expr_or_stmt_traversal_block_ptr tblock) /* This function is called from node_has_correct_lvalueness via traverse_expr. It checks the supplied node's direct operands for correct settings of the is_lvalue flag; if an incorrect setting is found, it sets tblock->result to TRUE and terminates the traversal. */ { if (!node_operands_have_correct_value_category(node)) { tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* check_node_operand_lvalueness */ a_boolean tree_has_correct_lvalueness(an_expr_node_ptr root) /* Return FALSE if any of the nodes in the expression tree rooted in root have their is_lvalue flag set incorrectly, TRUE otherwise. */ { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = check_node_operand_lvalueness; tblock.process_expressions_for_constants = TRUE; tblock.process_template_parameter_constants_and_expressions = TRUE; traverse_expr(root, &tblock); return !tblock.result; } /* tree_has_correct_lvalueness */ static a_boolean pointer_type_is_consistent(a_type_ptr ptr_type, a_type_ptr targ_type, a_boolean targ_type_is_prvalue) /* Check whether a pointer type is what it is supposed to be. Return FALSE if ptr_type is, in fact, not a pointer type (or a reference type -- in some cases, reference types are not lowered to pointer types) or if the type to which it points is not targ_type; return TRUE if the conditions are met. If targ_type_is_prvalue is TRUE, cv-qualification of the pointed-to type is ignored, except for C++ class rvalues. This is also used for C++/CLI handle operations. */ { a_boolean result = FALSE; a_boolean drop_qualifiers_under_ptr = targ_type_is_prvalue; if (is_any_ptr_or_ref_type(ptr_type)) { a_type_ptr pointed_to_type = type_pointed_to(ptr_type); if (!il_header.il_has_C_semantics && is_class_struct_union_type(pointed_to_type)) { /* In C++, keep qualifiers on class rvalues. */ drop_qualifiers_under_ptr = FALSE; } /* if */ if (drop_qualifiers_under_ptr) { pointed_to_type = make_unqualified_type(pointed_to_type); } /* if */ if (is_managed_nullptr_type(pointed_to_type) && is_standard_nullptr_type(targ_type)) { /* The Microsoft C++/CLI compiler gives the standard nullptr type as the result of indirection through a pointer to the managed nullptr type. */ result = TRUE; } else { result = identical_types(pointed_to_type, targ_type); } /* if */ #if PROTOTYPE_INSTANTIATIONS_IN_IL if (!result && (is_template_dependent_type(pointed_to_type) || is_template_dependent_type(targ_type))) { /* Consistency is not enforceable with generic operands. */ result = TRUE; } /* if */ } else if (is_template_dependent_type(ptr_type) || is_template_dependent_type(targ_type)) { /* With dependent types, the "pointer" type might not be a pointer. */ result = TRUE; #endif /* PROTOTYPE_INSTANTIATIONS_IN_IL */ } /* if */ return result; } /* pointer_type_is_consistent */ void check_operation_node_consistency(an_expr_node_ptr expr) /* Apply various consistency checks to expr, which must be an enk_operation node, and report any failure as an internal error. */ { an_expr_operator_kind op; an_expr_node_ptr operand_1; check_assertion(is_operation_node(expr)); op = expr->variant.operation.kind; operand_1 = expr->variant.operation.operands; /* This check couldn't be performed in standalone programs configured with PROTOTYPE_INSTANTIATIONS_IN_IL: generic operations and operands are not always lvalue-correct, but the code for detecting a dependent type is not available in a standalone program. */ if (!node_operands_have_correct_value_category(expr)) { /* At least one of the operands is an lvalue when an rvalue is expected or vice-versa. */ #if DEBUG db_expression(expr); #endif /* DEBUG */ internal_error("is_lvalue incorrectly set"); } /* if */ /* Check that eok_cast is not used for array-decay operations. (That was the pattern in an earlier version of the IL, but such conversions should now be represented by eok_array_to_pointer.) */ if (op == (an_expr_operator_kind)eok_cast && is_pointer_type(expr->type) && is_array_type(operand_1->type)) { #if DEBUG db_expression(expr); #endif /* DEBUG */ internal_error("eok_cast used for array-to-ptr decay"); } /* if */ /* Check that the result_is_not_used flags on the operands are not set when they should not be. (See also check_result_not_used_flag for the complementary processing, i.e., ensuring that the flags of the operands are set when they should be.) */ { an_expr_node_ptr op_node; for (op_node = operand_1; op_node != NULL; op_node = op_node->next) { if (op_node->result_is_not_used) { /* Usually, it's a bad thing if the value of an operand is not used by the operation, but check for special cases. */ if (op == (an_expr_operator_kind)eok_comma && (op_node == operand_1 || expr->result_is_not_used)) { /* Okay, this is an operand of a comma operation, and the flag is set correctly. */ } else if ((op == (an_expr_operator_kind)eok_question || op == (an_expr_operator_kind)eok_vector_question) && op_node != operand_1 && (expr->result_is_not_used || op_node->compiler_generated)) { /* Okay, this is an operand after the first on a "?" operation. Ordinarily, the eok_question node should also have the flag set, but when lowering an eok_question node with a throw-expression operand, that operand may be lowered to a construct (correctly) marked as "result_is_not_used" even when the other branch (and thus the whole "?" operation) may have its result used. The transformed operand will be marked as compiler-generated in such cases. */ } else if (op == (an_expr_operator_kind)eok_cast && expr->result_is_not_used && is_void_type(expr->type)) { /* Okay, this is a cast to void, and the flag is set correctly. */ } else if (op == (an_expr_operator_kind)eok_parens && expr->result_is_not_used) { /* Okay, this is the operand of a parenthesis node that is also not used, so the flag is set correctly. */ } else { /* The flag is set incorrectly. */ #if DEBUG db_expression(expr); db_expression(op_node); #endif /* DEBUG */ internal_error("result_is_not_used set wrong on operand"); } /* if */ } /* if */ } /* for */ } /* Check that the result and operand types are consistent for certain operators. (We cannot perform the check in a standalone program because it relies on identical_types, which is not available in that environment.) */ if (op == (an_expr_operator_kind)eok_address_of && !pointer_type_is_consistent(expr->type, operand_1->type, /*targ_type_is_prvalue=*/FALSE)) { #if DEBUG db_expression(expr); #endif /* DEBUG */ internal_error("wrong result type for &"); } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (op == (an_expr_operator_kind)eok_handle_to && !pointer_type_is_consistent(expr->type, operand_1->type, /*targ_type_is_prvalue=*/FALSE)) { #if DEBUG db_expression(expr); #endif /* DEBUG */ internal_error("wrong result type for %"); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (op == (an_expr_operator_kind)eok_subscript && !pointer_type_is_consistent( subscript_or_padd_pointer_operand(expr)->type, expr->type, /*targ_type_is_prvalue=*/!is_glvalue_node(expr))) { #if DEBUG db_expression(expr); #endif /* DEBUG */ internal_error("wrong result type for subscript"); } /* if */ if (op == (an_expr_operator_kind)eok_indirect) { a_type_ptr result_type = expr->type; if (!is_glvalue_node(expr) && is_pointer_type(result_type) && is_ptr_or_ref_type(operand_1->type) && is_function_type(type_pointed_to(operand_1->type))) { /* An lvalue-to-rvalue conversion on an expression with function type decays back to a pointer to function; we need to check against the function type itself. */ result_type = type_pointed_to(result_type); /* Drop cv-qualifiers on top of the function type. They would be ignored anyway. */ result_type = skip_typerefs(result_type); } /* if */ if (!pointer_type_is_consistent( operand_1->type, result_type, /*targ_type_is_prvalue=*/!is_glvalue_node(expr))) { #if DEBUG db_expression(expr); #endif /* DEBUG */ internal_error("wrong result type for *"); } /* if */ } /* if */ if (op == (an_expr_operator_kind)eok_add || op == (an_expr_operator_kind)eok_subtract || op == (an_expr_operator_kind)eok_multiply || op == (an_expr_operator_kind)eok_divide || op == (an_expr_operator_kind)eok_remainder) { a_type_ptr op_type_1 = skip_typerefs(operand_1->type); a_type_ptr op_type_2 = skip_typerefs(operand_1->next->type); if (!cast_identical_types(op_type_1, op_type_2) && /* Real and imaginary types can be intermixed provided their underlying representations are the same (this isn't true for mixed addition and subtraction, so those are excluded). */ !(is_floating_type(op_type_1) && is_floating_type(op_type_2) && op_type_1->variant.float_kind == op_type_2->variant.float_kind && (op != (an_expr_operator_kind)eok_add && op != ((an_expr_operator_kind)eok_subtract))) && #if FIXED_POINT_ALLOWED /* Disregard fixed point types (these can be mixed). */ !(fixed_point_enabled && (is_fixed_point_type(op_type_1) || is_fixed_point_type(op_type_2))) && #endif /* FIXED_POINT_ALLOWED */ #if GNU_VECTOR_TYPES_ALLOWED !(type_is(op_type_1, tk_vector) && type_is(op_type_2, tk_vector) && is_integral_type(op_type_1->variant.vector.element_type) && is_integral_type(op_type_2->variant.vector.element_type) && integral_types_the_same_except_for_signedness( op_type_1->variant.vector.element_type, op_type_2->variant.vector.element_type)) && #endif /* GNU_VECTOR_TYPES_ALLOWED */ /* Disregard template dependent operands. */ !(is_template_dependent_type(expr->type) || is_template_dependent_type(op_type_1) || is_template_dependent_type(op_type_2))) { /* The types of the two operands must be consistent. */ #if DEBUG db_expression(expr); #endif /* DEBUG */ internal_error("types for operation are not consistent"); } /* if */ } /* if */ #if GNU_VECTOR_TYPES_ALLOWED if (op == (an_expr_operator_kind)eok_vector_not || op == (an_expr_operator_kind)eok_vector_fill || op == (an_expr_operator_kind)eok_vector_eq || op == (an_expr_operator_kind)eok_vector_ne || op == (an_expr_operator_kind)eok_vector_lt || op == (an_expr_operator_kind)eok_vector_gt || op == (an_expr_operator_kind)eok_vector_le || op == (an_expr_operator_kind)eok_vector_ge || op == (an_expr_operator_kind)eok_vector_land || op == (an_expr_operator_kind)eok_vector_lor || op == (an_expr_operator_kind)eok_vector_question) { /* Vector operators return a vector type. */ if (expr->variant.operation.type_kind != (a_type_kind)tk_vector || !is_vector_type(expr->type)) { #if DEBUG db_expression(expr); #endif /* DEBUG */ internal_error("types for vector operation are not consistent"); } /* if */ if ((op == (an_expr_operator_kind)eok_vector_not || op == (an_expr_operator_kind)eok_vector_land || op == (an_expr_operator_kind)eok_vector_lor || op == (an_expr_operator_kind)eok_vector_question) && C_mode()) { /* These operations should only appear in C++ mode. */ #if DEBUG db_expression(expr); #endif /* DEBUG */ internal_error("vector operation in wrong mode"); } /* if */ } /* if */ #endif /* GNU_VECTOR_TYPES_ALLOWED */ } /* check_operation_node_consistency */ #endif /* !STANDALONE_UTILITY_PROGRAM */ void check_result_not_used_flag(an_expr_node_ptr node) /* node is an expression whose value is being discarded, e.g., it's a void expression. Check that the result_is_not_used flag is set for this node and, for certain operations, its operands. (The complementary processing, ensuring that the result_is_not_used_flag is NOT set when it should not be, is found in check_operation_node_consistency.) */ { if (!node->result_is_not_used) { internal_error("check_result_not_used_flag: flag is not set"); } /* if */ /* For some operations, subnodes get marked too. */ /* See comment in set_expr_result_not_used. */ if (node->kind == (an_expr_node_kind)enk_operation && is_void_type(node->type)) { an_expr_operator_kind op = node->variant.operation.kind; an_expr_node_ptr operand_1 = node->variant.operation.operands; if (op == (an_expr_operator_kind)eok_comma) { /* Given a comma operation, the second operand is not used if the entire operation is not used. */ check_result_not_used_flag(operand_1->next); } else if (op == (an_expr_operator_kind)eok_question || op == (an_expr_operator_kind)eok_vector_question) { /* Given a question mark operation, the second and third operands are not used if the entire operation is not used. */ check_result_not_used_flag(operand_1->next); check_result_not_used_flag(operand_1->next->next); } else if (op == (an_expr_operator_kind)eok_cast) { /* Given a cast to void, the operand is not used if the entire operation is not used. */ check_result_not_used_flag(operand_1); } else if (op == (an_expr_operator_kind)eok_parens) { /* Given parentheses, the operand is not used if the entire operation is not used. */ check_result_not_used_flag(operand_1); } /* if */ } /* if */ } /* check_result_not_used_flag */ #endif /* CHECKING */ a_string_view module_name_of(a_module_ptr mod) /* Given a named module, return the module name (excluding the partition if any). */ { /* If this assertion fails, the given module is not a named module or a partition thereof. */ check_assertion(mod->kind == mk_unit || mod->kind == mk_unit_partition); return skip_module_partitions(mod)->variant.unit.name; } /* module_name_of */ a_string_view module_partition_name_of(a_module_ptr mod) /* Given a named module partition, return the module partition name. */ { /* If this assertion fails, the given module is not a named module partition. */ check_assertion(mod->kind == mk_unit_partition); return mod->variant.unit_partition.name; } /* module_partition_name_of */ a_string module_full_name_of(a_module_ptr mod) /* Given a named module or named module partition, return the full name of the module including any partition name. */ { /* If this assertion fails, the given module is not a named module or a partition thereof. */ check_assertion(mod->kind == mk_unit || mod->kind == mk_unit_partition); a_string result = module_name_of(mod); if (mod->kind == mk_unit_partition) { result.append(":", module_partition_name_of(mod)); } /* if */ return result; } /* module_full_name_of */ a_string_view header_unit_name_of(a_module_ptr mod) /* Given a module header unit, return the name of the header as spelled between the angle brackets or quotes. */ { check_assertion(mod->kind == mk_header_unit); return mod->variant.header_unit.name; } /* header_unit_name_of */ a_boolean is_routine_definition_exported_inline(a_routine_ptr rp) /* Return TRUE if the given routine's definition needs to be exported (as the routine is either inline or constexpr); otherwise, return FALSE. */ { return rp->is_inline || rp->is_constexpr; } /* is_routine_definition_exported_inline */ /* Helper macro used by type_is_nonreal. Return TRUE if type is a nonreal class, nonreal alias template instantiation, or template parameter type. */ #define type_is_nonreal_no_typerefs(type) \ ((is_immediate_class_type(type) && \ (type)->variant.class_struct_union.is_nonreal_class) || \ (type->kind == (a_type_kind)tk_typeref && \ ((type)->variant.typeref.is_nonreal || \ (type)->variant.typeref.is_dependent)) || \ ((type)->kind == (a_type_kind)tk_template_param && !is_auto_type(type))) a_boolean type_is_nonreal(a_type_ptr type) /* Return TRUE if type is a nonreal class or typeref (i.e., for dependent template entities). */ { a_type_ptr type_without_typerefs = skip_typerefs(type); return (type_is_nonreal_no_typerefs(type) || ((type)->kind == (a_type_kind)tk_typeref && type_is_nonreal_no_typerefs(type_without_typerefs))); } /* type_is_nonreal */ a_boolean entity_is_nonreal(a_source_correspondence_ptr scp, an_il_entry_kind kind) /* Return TRUE if the entity specified by scp and kind is a prototype instantiation, a nonreal instantiation, or a template-dependent type. */ { a_boolean result = FALSE; if (kind == iek_type) { result = type_is_nonreal((a_type_ptr)scp); } else if (kind == iek_routine) { a_routine_ptr rout = (a_routine_ptr)scp; result = rout->is_prototype_instantiation; } else if (kind == iek_variable) { a_variable_ptr var = (a_variable_ptr)scp; result = var->is_prototype_instantiation || var->is_nonreal; } else if (kind == iek_constant) { a_constant_ptr cp = (a_constant_ptr)scp; result = (cp->kind) == (a_constant_repr_kind)ck_template_param; } /* if */ return result; } /* entity_is_nonreal */ #if !STANDALONE_UTILITY_PROGRAM #if DEBUG unsigned long db_show_il_c_fe_space_used(unsigned long grand_total) /* Display memory use for entities in front end memory in this file (il.c). */ { unsigned long num, size, total; db_space_used("based type fixups", num_based_type_fixups_allocated, a_based_type_fixup); db_space_used_lost("copy remap entries", avail_copy_remap_entries, num_copy_remap_entries_allocated, a_copy_remap_entry); return grand_total; } /* db_show_il_c_fe_space_used */ #endif /* DEBUG */ #if ENSURE_LOWERED_TYPE_LIST_ORDERING #if !DO_IL_LOWERING #error -- ENSURE_LOWERED_TYPE_LIST_ORDERING requires IL lowering #endif /* !DO_IL_LOWERING */ STATIC_THREAD a_type_ptr *type_reordering; /* A pointer to a temporary array of a_type_ptr values used to order the file-scope types list as required for the C-generating back end. See fix_type_list_ordering_problems. */ STATIC_THREAD a_type_ptr *next_type_reordering_slot; /* Pointer to the next available slot in type_reordering. */ /* Macro to append a given type to the type_reordering array. */ #define append_type_to_reordering(tp) \ (*next_type_reordering_slot++ = (tp)) static void process_type_for_ordering(a_type_ptr type, a_boolean must_be_complete); static void process_referenced_types_for_ordering(a_type_ptr type, a_boolean must_be_complete); static void process_referenced_type_for_ordering(a_type_ptr type, a_boolean must_be_complete) /* The indicated type is referenced from another type (ultimately from a type on the file-scope types list). If it's a type that is on the file-scope types list, add it to the type_reordering array if appropriate. Specifically, it is added if the C-generating back end requires it to have been rendered as a definition before rendering the type that references "type". Whether "type" is added to type_reordering or not, types referenced by "type" are recursively processed if needed. If must_be_complete is TRUE, "type" is referenced in a way that requires it to be complete. */ { a_scope_ptr scope = parent_scope_of(type); a_boolean in_func_proto = scope != NULL && scope->kind == (a_scope_kind)sck_func_prototype; /* Note that types defined in function prototypes are not promoted to the file scope. */ if (type->type_processed_as_complete_for_ordering || (type->type_processed_for_ordering && !must_be_complete)) { /* The type has either (a) already been processed as requiring completeness (and therefore doesn't need traversing again), or (b) been processed without requiring completeness but this call doesn't require completeness either (and therefore an additional traversal would have no effect). */ } else if (is_immediate_class_type(type) && !in_func_proto) { /* This is a struct or union, which goes on the file-scope types list. */ /* structs and unions are declared in the first pass through the types in c_gen_be, so their names are always available. So if no definition is needed (must_be_complete is FALSE), don't append the type yet to avoid circular dependency problems. */ if (must_be_complete) { process_type_for_ordering(type, /*must_be_complete=*/TRUE); } /* if */ } else if (is_immediate_enum_type(type) && !in_func_proto) { /* This is an enum, which goes on the file-scope types list. */ /* enums are put out as definitions in the first pass in c_gen_be, so they are always available. However, it doesn't hurt to append the type now to the reordered list. */ process_type_for_ordering(type, /*must_be_complete=*/TRUE); } else if (type_is_typedef(type) && !type->type_processed_for_ordering && type->process_for_ordering && !in_func_proto) { /* This is a typedef that is on the file-scope types list and that is not on the reordering yet. */ /* Typedefs are put out as definitions in the second pass in c_gen_be, so they are available -- even as incomplete types -- only after their appearance in the type list: Add this entry to the reordering now. */ process_type_for_ordering(type, must_be_complete); } else { /* This type is one that doesn't itself go on the file-scope types list, or a typedef type that is already placed in the new ordering but that has not yet been traversed as requiring completeness. A typedef that is not on the file-scope types list (e.g., one synthesized for an intrinsically-resolved xyz::member) is rendered transparently by c_gen_be, so it is treated transparently here too: Its underlying type is processed so that, if that type is on the file-scope list and must be complete, it is ordered before the type that refers to it through the typedef. */ type->type_processed_for_ordering = TRUE; if (must_be_complete) { type->type_processed_as_complete_for_ordering = TRUE; } /* if */ process_referenced_types_for_ordering(type, must_be_complete); } /* if */ } /* process_referenced_type_for_ordering */ static void process_referenced_types_for_ordering(a_type_ptr type, a_boolean must_be_complete) /* Process any types referenced by the indicated type as being referenced by the ordering processing. "type" itself is not processed at this level. See process_referenced_type_for_ordering for the description of must_be_complete. */ { switch (type->kind) { case tk_typeref: /* A typedef or cv-qualifier. Process the underlying type. */ if (!must_be_complete && type_is_typedef(type) && is_array_type(type) && !type->type_processed_for_ordering) { /* Array typedefs must be complete in C. */ process_type_for_ordering(type, /*must_be_complete=*/TRUE); } else if (!ignore_type_in_back_end(type)) { process_referenced_type_for_ordering(type->variant.typeref.type, must_be_complete); } /* if */ break; case tk_pointer: /* A pointer type. Process the underlying type, which typically does not need to be complete. That appears not to be true for pointers to arrays as later versions of GCC (4.0.0 and later) and clang both require a complete type in that case. */ process_referenced_type_for_ordering(type->variant.pointer.type, is_array_type( type->variant.pointer.type)); break; case tk_array: /* An array type. Process the underlying type. */ process_referenced_type_for_ordering(type->variant.array.element_type, must_be_complete); break; case tk_routine: /* A function type. The return type and the parameter types do not have to be complete. */ process_referenced_type_for_ordering(type->variant.routine.return_type, /*must_be_complete=*/FALSE); { a_routine_type_supplement_ptr rtsp = type->variant.routine.extra_info; a_param_type_ptr ptp; for (ptp = rtsp->param_type_list; ptp != NULL; ptp = ptp->next) { process_referenced_type_for_ordering(ptp->type, /*must_be_complete=*/FALSE); } /* for */ } break; case tk_struct: case tk_union: if (must_be_complete && !ignore_type_in_back_end(type)) { /* struct or union type. Process the member types. */ a_field_ptr field; for (field = type->variant.class_struct_union.field_list; field != NULL; field = field->next) { process_referenced_type_for_ordering(field->type, must_be_complete); } /* for */ } /* if */ break; default: /* No processing */ break; } /* switch */ } /* process_referenced_types_for_ordering */ static void process_type_for_ordering(a_type_ptr type, a_boolean must_be_complete) /* Add the indicated type to the type_reordering array. Before doing that, make sure that all types referenced by the type are already in that array if needed. If must_be_complete is TRUE, the type is used in a way that requires it to be complete. The type passed in must be one that appears on the file-scope types list (i.e., struct, union, enum, or typedef). */ { a_boolean append_type_early = FALSE; if (!type->process_for_ordering) goto done; /* Set the flags indicating that this type has been processed before actually traversing the subtree to avoid unnecessary recursion. */ check_assertion(!type->type_processed_for_ordering); type->type_processed_for_ordering = TRUE; if (must_be_complete) { type->type_processed_as_complete_for_ordering = TRUE; if (type_is_typedef(type)) { a_type_ptr utype = skip_typerefs_not_typedefs(type->variant.typeref.type); if (is_immediate_class_type(utype)) { /* A typedef for a class type and we are traversing it completely. Emit the typedef before emitting any required types for the class and its members, because otherwise we may end up needing the typedef for one of its members and we will already have committed to emitting it later */ append_type_early = TRUE; } /* if */ } /* if */ } /* if */ if (append_type_early) { /* Add the type to the list of processed types before adding the class type that it depends on. */ append_type_to_reordering(type); process_referenced_types_for_ordering(type, must_be_complete); } else { /* Process any types referenced from this type before adding the type itself. */ process_referenced_types_for_ordering(type, must_be_complete); append_type_to_reordering(type); } /* if */ done:; } /* process_type_for_ordering */ void fix_type_list_ordering_problems(void) /* Fix any ordering problems on the file scope types list that would cause errors when C code is generated by the C-generating back end. This code runs after IL lowering. */ { if (il_header.primary_scope->types != NULL) { a_type_ptr type = il_header.primary_scope->types; unsigned long n_types = 0, k; /* Run through the file-scope types list. Move each type to a list of processed types, making sure that all the types it references are processed previously so that they will precede the type on the list (if they ought to be on the list at all). */ /* First count the types to be reordered and allocate an array of a_type_ptr values to keep track of the new order. Mark the flags that are on the list. Only those types will be considered when encountered for ordering. */ for (; type != NULL; type = type->next) { type->process_for_ordering = TRUE; ++n_types; } /* for */ type_reordering = (a_type_ptr*)alloc_general(n_types*sizeof(a_type_ptr)); next_type_reordering_slot = type_reordering; /* We're modeling references in types output in the second pass of c_gen_be here. In the first pass, structs and unions are output as declarations, and enums as definitions, so their names are always available in the second pass, but the definitions of structs and unions are not available unless they appear earlier in the list. typedefs are put out in the second pass, so their names are not available unless they appear earlier in the list. However, their types need not be complete at the point of definition. */ for (type = il_header.primary_scope->types; type != NULL; type = type->next) { if (!type->type_processed_for_ordering) { process_type_for_ordering(type, /*must_be_complete=*/ (is_immediate_class_type(type) || is_immediate_enum_type(type))); } /* if */ } /* for */ #if DEBUG { unsigned long new_n_types = ((unsigned long)(next_type_reordering_slot - type_reordering)); if (new_n_types != n_types && db_flag_is_set("ftlop")) { type = il_header.primary_scope->types; fprintf(f_debug, "Types for reordering: \n"); for (; type != NULL; type = type->next) { db_type_name(type); fprintf(f_debug, "\n"); } /* if */ fprintf(f_debug, "Types being addded: \n"); for (k = 0; k < new_n_types; ++k) { db_type_name(type_reordering[k]); fprintf(f_debug, "\n"); } /* for */ } /* if */ } /* if */ #endif /* DEBUG */ check_assertion( (unsigned long)(next_type_reordering_slot - type_reordering) == n_types); /* Now apply the reordering. */ il_header.primary_scope->types = type_reordering[0]; for (k = 1; knext = type_reordering[k]; } /* for */ type_reordering[k-1]->next = NULL; translation_units->file_scope_pointers_block.last_type = type_reordering[k-1]; free_general(type_reordering, n_types*sizeof(a_type_ptr)); type_reordering = NULL; } /* if */ } /* fix_type_list_ordering_problems */ #endif /* ENSURE_LOWERED_TYPE_LIST_ORDERING */ #if MODULE_ID_NEEDED STATIC_THREAD a_source_correspondence_ptr module_id_scp; /* The entity that is being used as the basis for the translation unit's module id. If the entity is defined in a PCH file, module_id_scp will still point to the entity after the PCH is restored (but the translation unit filename may be different). */ STATIC_THREAD an_il_entry_kind module_id_kind; /* The entity kind for module_id_scp above. */ void use_variable_or_routine_for_module_id_if_needed( a_source_correspondence_ptr scp, an_il_entry_kind kind) /* If a module id has not been selected for the current translation unit, see if the routine or variable referred to by scp (of kind iek_routine or iek_variable respectively) can be used as the basis for a repeatable module id that will be unique across the entire program. Generally speaking, a routine or variable definition with unspecified storage class at the file/namespace/class scope can be used, but there are exceptions. */ { if (get_module_id() == NULL) { if (module_id_scp == NULL) { /* Normal case. */ check_assertion(scp != NULL && (kind == (an_il_entry_kind)iek_variable || kind == (an_il_entry_kind)iek_routine)); if ((scp->parent_scope == il_header.primary_scope || (scp_is_class_or_namespace_member(scp) && !is_member_of_unnamed_namespace(scp))) && !seq_is_in_system_header(scp->decl_position.seq)) { /* Only consider definitions in file/namespace/class scopes. */ if (kind == (an_il_entry_kind)iek_variable) { a_variable_ptr variable = (a_variable_ptr)scp; if (variable->storage_class != (a_storage_class)sc_unspecified || variable->init_kind == (an_init_kind)initk_none) { /* Only consider variables that are defined. Make sure that the init_kind is not none -- this eliminates tentative definitions. */ } else if (variable->is_template_variable) { /* Don't use template variables or static data members. Some implementations may generate these in multiple files. */ } else if (variable->is_inline) { /* An inline variable can appear in multiple translation units. */ #if GNU_EXTENSIONS_ALLOWED } else if (variable->is_weak) { /* Weak variable definitions may appear in multiple translation units. We therefore don't consider them for use in the module id. */ #endif /* GNU_EXTENSIONS_ALLOWED */ #if MICROSOFT_EXTENSIONS_ALLOWED } else if (variable->decl_modifiers & DM_SELECTANY){ /* Variables defined as "selectany" may be defined in multiple translation units. Do not use them for the module id. */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else if (variable->declared_with_auto_type_specifier) { /* The type for this variable has not yet been determined (so its mangled name cannot be determined yet). */ } else { module_id_scp = scp; module_id_kind = iek_variable; } /* if */ } else if (kind == (an_il_entry_kind)iek_routine) { a_routine_ptr routine = (a_routine_ptr)scp; if (routine->storage_class != (a_storage_class)sc_unspecified || routine->is_inline) { /* Only external, non-inline routines may be used for a module id. Note that this check also disqualifies routines that may be externalized in the future (they have static linkage while the declaration is being scanned). */ } else if (routine->is_template_function) { /* Don't use template functions. Some implementations may generate these in multiple files. */ #if GNU_EXTENSIONS_ALLOWED } else if (routine->is_weak) { /* Weak routine definitions may appear in multiple translation units. We therefore don't consider them for use in the module id. */ #endif /* GNU_EXTENSIONS_ALLOWED */ } else if (is_or_contains_unnamed_namespace_type(routine->type)) { /* Make sure its type does not involve an unnamed namespace (otherwise its mangled name would involve the module id). */ } else if (routine_contains_an_individuated_entity(routine)) { /* Make sure the routine is not an individuated entity nor contains an individuated entity as part of its type. (otherwise its mangled name would involve the module id). */ #if GNU_FUNCTION_MULTIVERSIONING } else if (has_gnu_routine_supp(routine) && (gnu_routine_supp(routine)->is_representative || gnu_routine_supp(routine)->is_target_specific_version)) { /* Skip GNU function multiversion routines (the mangling for these can depend on their use, making them poor candidates for basing a module id on). */ #endif /* GNU_FUNCTION_MULTIVERSIONING */ } else if (is_auto_type(return_type_of(routine->type))) { /* The return type for this routine has not yet been determined (so its mangled name cannot be determined yet). */ } else { /* This routine definition fits the bill. */ module_id_scp = scp; module_id_kind = iek_routine; } /* if */ } /* if */ } /* if */ } else { /* We've previously selected an entity to base the module id on, but no module id exists yet. This can happen when the module id entity is defined in a precompiled header. In that case, define the module id now (based on the current translation unit's filename, not that of the translation unit when the PCH was created). */ check_assertion(use_precompiled_header || automatic_pch_processing); } /* if */ if (module_id_scp != NULL) { /* We've found a suitable candidate; create the module id for the translation unit. */ a_const_char *name = NULL; if (module_id_kind == iek_variable) { /* If the variable is a class or namespace member, get its mangled name; otherwise use its unmangled name. */ if (scp_is_class_or_namespace_member(module_id_scp)) { name = get_mangled_variable_name((a_variable_ptr)module_id_scp); } else { name = module_id_scp->name; } /* if */ } else { check_assertion(module_id_kind == iek_routine); if (C_mode()) { name = module_id_scp->name; } else { name = get_mangled_function_name((a_routine_ptr)module_id_scp); } /* if */ } /* if */ check_assertion(name != NULL); (void)make_module_id(name); } /* if */ } /* if */ } /* use_variable_or_routine_for_module_id_if_needed */ #endif /* MODULE_ID_NEEDED */ #endif /* !STANDALONE_UTILITY_PROGRAM */ void destination_type_for_reference_cast(an_expr_node_ptr expr, a_type *ref_type) /* The type of an (rvalue) reference cast operation doesn't reflect the (rvalue) reference, but that type is needed both for mangling as well as in the C++ generating back end. This routine returns, in *ref_type, the reference type that appeared in the source code. Note that this routine uses a pointer to a_type parameter that is supplied by the caller rather than simply constructing and returning a type so that it can be used by back ends. */ { a_type_ptr dest_type = expr->type; check_assertion(ref_type != NULL && is_operation_node(expr) && (expr->variant.operation.is_reference_cast || node_operator_is(expr, eok_ref_cast) || node_operator_is(expr, eok_ref_dynamic_cast))); /* A cast to a reference type. */ if (!is_glvalue_node(expr)) { /* The cast has a glvalue-to-prvalue conversion built in, so the node type may be a little different from the underlying cast type. Use the type saved when the reference cast was recorded. */ dest_type = expr->orig_lvalue_type; check_assertion(dest_type != NULL); } /* if */ /* Substitute a reference type for the destination type. */ #if !STANDALONE_UTILITY_PROGRAM clear_type(ref_type, (a_type_kind)tk_pointer); #else /* STANDALONE_UTILITY_PROGRAM */ /* Again, we can't use clear_type, so we zero the struct and then set the kind. */ memzero((char *)ref_type, sizeof(*ref_type)); ref_type->kind = (a_type_kind)tk_pointer; #endif /* !STANDALONE_UTILITY_PROGRAM */ ref_type->variant.pointer.is_reference = TRUE; if (expr->variant.operation.is_rvalue_reference_cast) { ref_type->variant.pointer.is_rvalue_reference = TRUE; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (expr->variant.operation.is_tracking_reference_cast) { /* A cast to a C++/CLI tracking reference. */ ref_type->variant.pointer.is_handle = TRUE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ ref_type->variant.pointer.type = dest_type; } /* destination_type_for_reference_cast */ an_expr_node_ptr expr_before_type_adjustment(an_expr_node_ptr expr) /* expr is an expression that may have been passed through adjust_glvalue_type or adjust_class_rvalue_type to adjust its type. If it is, strip off the adjustment to get back to the original unadjusted lvalue expression, and return that. If not, return the original expression. */ { an_expr_node_ptr texpr = expr; /* A type adjustment is zero or more base class casts followed optionally by an lvalue or rvalue type adjustment to adjust the cv-qualification. That's based on what adjust_glvalue_type and adjust_class_rvalue_type do, and the code here would have to change if those routines change. */ /* No skip_parens needed here because the sequence we are looking for is compiler-generated. */ while (is_operation_node(texpr) && (node_operator_is(texpr, eok_class_rvalue_adjust) || (node_operator_is(texpr, eok_lvalue_adjust)))) { a_type_ptr expr_type = skip_typerefs(texpr->type); a_type_ptr opnd_type = skip_typerefs(texpr->variant.operation.operands->type); if (standalone_identical_types(expr_type, opnd_type)) { /* Drop a cv-qualification-adjusting operator. */ texpr = texpr->variant.operation.operands; } else { break; } /* if */ } /* while */ while (is_operation_node(texpr) && texpr->compiler_generated && node_operator_is(texpr, eok_base_class_cast)) { /* Drop base class casts. */ texpr = texpr->variant.operation.operands; } /* while */ return texpr; } /* expr_before_type_adjustment */ a_boolean pm_constant_is_null(a_constant_ptr constant) /* constant is a pointer-to-member constant. Return TRUE if it is a NULL constant. */ { a_boolean is_null = constant->variant.ptr_to_member.is_function_ptr ? (constant->variant.ptr_to_member.variant.routine == NULL) : (constant->variant.ptr_to_member.variant.field == NULL); return is_null; } /* pm_constant_is_null */ void walk_parents(a_source_correspondence *scp, an_il_entry_kind kind, a_walk_parent_callback callback, a_walk_parents_control_block *wpcb, a_walk_parents_flag_set options) /* This routine walks through each of the parents of scp in turn (from innermost to outermost) and invokes the callback routine on each of its parents (subject to the flags set in "options"). If non-NULL, wpcb specifies a control block that is passed to the callback routine. wpcb->terminate is initialized to FALSE, and if ever set to TRUE by the callback routine, the walk is terminated. The options flags can be used to select which parent entities are of interest (e.g., WP_ROUTINE would result in callbacks only for routines to which scp is local). See the descriptions of each of the flags for more information. */ { a_walk_parents_flag_set flag = WP_NO_INPUT_FLAGS; if (wpcb != NULL) { wpcb->terminate = FALSE; } /* if */ if ((options & WP_SELF) != 0) { /* Note that the callback is called regardless of other flags, so a callback routine should be ready to handle this case. */ callback(scp, kind, wpcb); } /* if */ if (wpcb == NULL || !wpcb->terminate) { while ((scp->parent_scope != NULL && scp->parent_scope->kind != (a_scope_kind)sck_file) || scp->is_local_to_function) { if (scp->enclosing_routine != NULL) { scp = &scp->enclosing_routine->source_corresp; kind = iek_routine; flag = WP_ROUTINE; } else if (scp->is_class_member) { a_type_ptr parent_class; check_assertion(scp->parent_scope != NULL); parent_class = scp_parent_class(scp); check_assertion(parent_class != NULL); scp = &parent_class->source_corresp; kind = iek_type; flag = WP_TYPE; } else if (kind == iek_type && ((a_type_ptr)scp)->kind == (a_type_kind)tk_template_param) { /* Skip template parameters (whose scopes are template declarations).*/ break; } else { a_namespace_ptr nsp = scp_parent_namespace_or_null(scp); check_assertion(nsp != NULL); scp = &nsp->source_corresp; kind = iek_namespace; flag = WP_NAMESPACE; } /* if */ if ((flag & options) != 0) { callback(scp, kind, wpcb); } /* if */ if (wpcb != NULL && wpcb->terminate) { /* Callback routine signals to terminate. */ break; } /* if */ } /* while */ } /* if */ } /* walk_parents */ void eval_order_for_op_kind(an_opname_kind kind, a_boolean *eval_left_to_right, a_boolean *eval_right_to_left) /* For the given operation kind, determine, and return (in *eval_left_to_right and *eval_right_to_left), the evaluation order for operands of the operation. */ { *eval_left_to_right = FALSE; *eval_right_to_left = FALSE; switch (kind) { case onk_assign: case onk_plus_assign: case onk_minus_assign: case onk_times_assign: case onk_divide_assign: case onk_remainder_assign: case onk_excl_or_assign: case onk_and_assign: case onk_or_assign: case onk_shift_left_assign: case onk_shift_right_assign: *eval_right_to_left = TRUE; break; case onk_shift_left: case onk_shift_right: case onk_arrow_star: case onk_subscript: case onk_function_call: *eval_left_to_right = TRUE; break; default: /* No mandated evaluation order. */ break; } /* switch */ } /* eval_order_for_op_kind */ void eval_order_for_binary_node_kind(an_expr_operator_kind kind, a_boolean *eval_left_to_right, a_boolean *eval_right_to_left) /* For the given expression node kind, determine whether the binary expression should have its operands evaluated from left-to-right or right-to-left and set the appropriate flag. Not used for call operations as those may be sequenced either way. */ { *eval_left_to_right = FALSE; *eval_right_to_left = FALSE; switch (kind) { case eok_assign: case eok_add_assign: case eok_subtract_assign: case eok_multiply_assign: case eok_divide_assign: case eok_remainder_assign: case eok_shiftl_assign: case eok_shiftr_assign: case eok_and_assign: case eok_or_assign: case eok_xor_assign: case eok_padd_assign: case eok_psubtract_assign: case eok_bassign: *eval_right_to_left = TRUE; break; case eok_subscript: case eok_shiftl: case eok_shiftr: *eval_left_to_right = TRUE; break; case eok_call: case eok_dot_member_call: case eok_points_to_member_call: case eok_dot_pm_call: case eok_points_to_pm_call: /* These may be set either way and should be handled outside of this routine. */ unexpected_condition(); break; default: /* No mandated evaluation order. */ break; } /* switch */ } /* eval_order_for_binary_node_kind */ #if !STANDALONE_UTILITY_PROGRAM void il_one_time_init(void) /* Do one-time initialization of variables related to the IL. (Variables that need to be reinitialized with each new translation unit are handled in il_init.) */ { /* Static variables in il.c: */ seq_number_lookup_table_size = 0; seq_number_lookup_table = NULL; okay_to_use_seq_number_lookup_table = TRUE; expr_hash_value = 0; /* Initialize certain global variables declared in il.h. */ #if DEBUG db_name_str_buffer = NULL; db_qualifiers_str_buffer = NULL; #endif /* DEBUG */ /* Save variables from il.h and il.c that are needed for precompiled headers */ if (precompiled_header_processing_required) { STATIC_THREAD a_pch_saved_variable saved_vars[] = { pch_saved_var_array_elem(type_of_type_info), pch_array_saved_var_array_elem(types_of_type_info), #if MICROSOFT_EXTENSIONS_ALLOWED pch_saved_var_array_elem(type_of_guid), pch_saved_var_array_elem(idisposable_dispose_routine), pch_saved_var_array_elem(object_finalize_routine), #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ pch_array_saved_var_array_elem(float_types), pch_saved_var_array_elem(il_error_type), pch_saved_var_array_elem(il_unknown_type), pch_saved_var_array_elem(il_void_type), pch_saved_var_array_elem(il_reflection_type), pch_saved_var_array_elem(il_std_string_view), pch_saved_var_array_elem(il_wchar_t_type), pch_saved_var_array_elem(il_char8_t_type), pch_saved_var_array_elem(il_char16_t_type), pch_saved_var_array_elem(il_char32_t_type), pch_saved_var_array_elem(il_bool_type), pch_saved_var_array_elem(il_scalable_vector_count_type), pch_saved_var_array_elem(il_modal_8bit_floating_point_type), pch_saved_var_array_elem(il_float8e4m3_type), pch_saved_var_array_elem(il_float8e5m2_type), pch_saved_var_array_elem(il_standard_nullptr_type), pch_saved_var_array_elem(il_managed_nullptr_type), pch_saved_var_array_elem(il_strong_ordering_type), pch_saved_var_array_elem(il_weak_ordering_type), pch_saved_var_array_elem(il_partial_ordering_type), pch_saved_var_array_elem(il_strong_equality_type), pch_saved_var_array_elem(il_weak_equality_type), pch_saved_var_array_elem(il_source_location_impl_type), pch_saved_var_array_elem(il_source_location_fields), pch_array_saved_var_array_elem(int_types), pch_array_saved_var_array_elem(signed_int_types), #if MICROSOFT_EXTENSIONS_ALLOWED pch_array_saved_var_array_elem(microsoft_sized_int_types), pch_array_saved_var_array_elem(microsoft_sized_signed_int_types), #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if FIXED_POINT_ALLOWED pch_array_saved_var_array_elem(fixed_point_types), #endif /* FIXED_POINT_ALLOWED */ #if C99_IL_EXTENSIONS_SUPPORTED pch_array_saved_var_array_elem(complex_types), pch_array_saved_var_array_elem(imaginary_types), #endif /* C99_IL_EXTENSIONS_SUPPORTED */ pch_saved_var_array_elem(string_types), #if NAMED_REGISTERS_ALLOWED pch_array_saved_var_array_elem(named_register_variables), #endif /* NAMED_REGISTERS_ALLOWED */ pch_saved_var_array_elem(shareable_constants_table), pch_saved_var_array_elem(curr_object_lifetime), #if ORPHAN_PROCESSING_NEEDED pch_array_saved_var_array_elem(orphaned_file_scope_il_entries), #endif /* ORPHAN_PROCESSING_NEEDED */ pch_saved_var_array_elem(curr_fp_contract_state), pch_saved_var_array_elem(curr_fenv_access_state), pch_saved_var_array_elem(curr_cx_limited_range_state), #if FIXED_POINT_ALLOWED pch_saved_var_array_elem(curr_fx_full_precision_state), pch_saved_var_array_elem(curr_fx_fract_overflow_state), pch_saved_var_array_elem(curr_fx_accum_overflow_state), #endif /* FIXED_POINT_ALLOWED */ pch_saved_var_array_elem(curr_seq_number_lookup_entry), /* Don't save seq_number_lookup_table because it points to general memory. */ #if UPC_EXTENSIONS_ALLOWED pch_saved_var_array_elem(curr_upc_access_method), pch_saved_var_array_elem(max_upc_block_size), #endif /* UPC_EXTENSIONS_ALLOWED */ #if DEBUG pch_saved_var_array_elem(num_searches_for_shareable_constants), pch_saved_var_array_elem(num_compares_for_shareable_constants), pch_saved_var_array_elem(num_func_shareable_constants), pch_saved_var_array_elem(num_get_based_type_calls), pch_saved_var_array_elem(num_shareable_constants), pch_saved_var_array_elem(num_used_shareable_constant_buckets), pch_saved_var_array_elem(num_based_type_fixups_allocated), pch_saved_var_array_elem(num_copy_remap_entries_allocated), #endif /* DEBUG */ pch_saved_var_array_elem(avail_copy_remap_entries), #if MODULE_ID_NEEDED pch_saved_var_array_elem(module_id_scp), pch_saved_var_array_elem(module_id_kind), #endif /* MODULE_ID_NEEDED */ pch_saved_var_array_elem(last_routine_scope_variable_found), pch_saved_var_array_elem(strong_ordering_equal), pch_saved_var_array_elem(strong_ordering_less), pch_saved_var_array_elem(strong_ordering_greater), pch_saved_var_array_elem(weak_ordering_equivalent), pch_saved_var_array_elem(weak_ordering_less), pch_saved_var_array_elem(weak_ordering_greater), pch_saved_var_array_elem(partial_ordering_equivalent), pch_saved_var_array_elem(partial_ordering_less), pch_saved_var_array_elem(partial_ordering_greater), pch_saved_var_array_elem(partial_ordering_unordered), pch_saved_var_array_elem(strong_equality_equal), pch_saved_var_array_elem(strong_equality_nonequal), pch_saved_var_array_elem(weak_equality_equivalent), pch_saved_var_array_elem(weak_equality_nonequivalent), pch_saved_var_array_terminating_elem() }; register_pch_saved_variables(saved_vars); } /* if */ /* Register variables (and arrays) that have distinct copies for distinct compilation units. */ register_trans_unit_variable(curr_il_region_number); register_trans_unit_array(int_types); register_trans_unit_array(signed_int_types); #if MICROSOFT_EXTENSIONS_ALLOWED register_trans_unit_array(microsoft_sized_int_types); register_trans_unit_array(microsoft_sized_signed_int_types); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if FIXED_POINT_ALLOWED register_trans_unit_array(fixed_point_types); #endif /* FIXED_POINT_ALLOWED */ register_trans_unit_array(float_types); #if C99_IL_EXTENSIONS_SUPPORTED register_trans_unit_array(complex_types); register_trans_unit_array(imaginary_types); #endif /* C99_IL_EXTENSIONS_SUPPORTED */ register_trans_unit_variable(string_types); #if NAMED_REGISTERS_ALLOWED register_trans_unit_array(named_register_variables); #endif /* NAMED_REGISTERS_ALLOWED */ register_trans_unit_variable(il_error_type); register_trans_unit_variable(il_unknown_type); register_trans_unit_variable(il_void_type); register_trans_unit_variable(il_reflection_type); register_trans_unit_variable(il_std_string_view); register_trans_unit_variable(il_wchar_t_type); register_trans_unit_variable(il_char8_t_type); register_trans_unit_variable(il_char16_t_type); register_trans_unit_variable(il_char32_t_type); register_trans_unit_variable(il_bool_type); register_trans_unit_variable(il_scalable_vector_count_type); register_trans_unit_variable(il_modal_8bit_floating_point_type); register_trans_unit_variable(il_float8e4m3_type); register_trans_unit_variable(il_float8e5m2_type); register_trans_unit_variable(il_standard_nullptr_type); register_trans_unit_variable(il_managed_nullptr_type); register_trans_unit_variable(il_strong_ordering_type), register_trans_unit_variable(il_weak_ordering_type), register_trans_unit_variable(il_partial_ordering_type), register_trans_unit_variable(il_strong_equality_type), register_trans_unit_variable(il_weak_equality_type), register_trans_unit_variable(il_source_location_impl_type), register_trans_unit_variable(il_source_location_fields), register_trans_unit_variable(shareable_constants_table); register_trans_unit_variable(seq_cache); register_trans_unit_variable(effective_primary_source_file); /* Global variables declared in il.h. */ register_trans_unit_array_with_field(orphaned_file_scope_il_entries, orphaned_file_scope_il_entries); register_trans_unit_variable(type_of_type_info); register_trans_unit_array(types_of_type_info); #if MICROSOFT_EXTENSIONS_ALLOWED register_trans_unit_variable(type_of_guid); register_trans_unit_variable(idisposable_dispose_routine); register_trans_unit_variable(object_finalize_routine); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ register_trans_unit_variable(curr_fp_contract_state); register_trans_unit_variable(curr_fenv_access_state); register_trans_unit_variable(curr_cx_limited_range_state); #if FIXED_POINT_ALLOWED register_trans_unit_variable(curr_fx_full_precision_state), register_trans_unit_variable(curr_fx_fract_overflow_state), register_trans_unit_variable(curr_fx_accum_overflow_state), #endif /* FIXED_POINT_ALLOWED */ register_trans_unit_variable(curr_object_lifetime); register_trans_unit_variable(okay_to_eliminate_unneeded_il_entries); /* Not conditional because it's also used by trans_copy.c: */ register_trans_unit_variable(initial_value_for_il_lowering_flag); #if UPC_EXTENSIONS_ALLOWED register_trans_unit_variable(curr_upc_access_method); #endif /* UPC_EXTENSIONS_ALLOWED */ register_trans_unit_variable(scheduled_routine_moves); register_trans_unit_variable(scope_of_scheduled_routine_moves); register_trans_unit_variable(scope_pointers_of_scheduled_routine_moves); register_trans_unit_variable(routine_move_placeholders); register_trans_unit_variable(n_scheduled_routine_moves); #if MODULE_ID_NEEDED register_trans_unit_variable(module_id_scp); register_trans_unit_variable(module_id_kind); #endif /* MODULE_ID_NEEDED */ register_trans_unit_variable(strong_ordering_equal); register_trans_unit_variable(strong_ordering_less); register_trans_unit_variable(strong_ordering_greater); register_trans_unit_variable(weak_ordering_equivalent), register_trans_unit_variable(weak_ordering_less), register_trans_unit_variable(weak_ordering_greater), register_trans_unit_variable(partial_ordering_equivalent); register_trans_unit_variable(partial_ordering_less); register_trans_unit_variable(partial_ordering_greater); register_trans_unit_variable(partial_ordering_unordered); register_trans_unit_variable(strong_equality_equal); register_trans_unit_variable(strong_equality_nonequal); register_trans_unit_variable(weak_equality_equivalent); register_trans_unit_variable(weak_equality_nonequivalent); il_alloc_one_time_init(); } /* il_one_time_init */ void il_trans_unit_init(void) /* Initialize static variables related to the IL. These are variables that need initialization for every (primary and secondary) translation unit. */ { int i; /* Global variables declared in il.h. */ curr_il_region_number = NULL_region_number; type_of_type_info = NULL; for (i = 0; i < (int)tik_last; ++i) { types_of_type_info[i] = NULL; } /* for */ #if MICROSOFT_EXTENSIONS_ALLOWED type_of_guid = NULL; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ curr_fp_contract_state = (a_stdc_pragma_value)stdc_pv_default; curr_fenv_access_state = (a_stdc_pragma_value)stdc_pv_default; curr_cx_limited_range_state = (a_stdc_pragma_value)stdc_pv_default; #if FIXED_POINT_ALLOWED curr_fx_full_precision_state = (a_stdc_pragma_value)stdc_pv_default; curr_fx_fract_overflow_state = (a_stdc_pragma_value)stdc_pv_default; curr_fx_accum_overflow_state = (a_stdc_pragma_value)stdc_pv_default; #endif /* FIXED_POINT_ALLOWED */ #if UPC_EXTENSIONS_ALLOWED curr_upc_access_method = (a_upc_access_method)upc_access_unspecified; #endif /* UPC_EXTENSIONS_ALLOWED */ curr_object_lifetime = NULL; /* remove_unneeded_entities is the value, settable from the command line, to which okay_to_eliminate_unneeded_il_entries should be initialized with each new compilation unit. */ okay_to_eliminate_unneeded_il_entries = remove_unneeded_entities; /* Not conditional because it's also used by trans_copy.c: */ initial_value_for_il_lowering_flag = 0; effective_primary_source_file = NULL; #if ONE_INSTANTIATION_PER_OBJECT needed_flag_bit_number = 0; #endif /* ONE_INSTANTIATION_PER_OBJECT */ /* Static variables declared in il.c. */ /* Depending on NULL represented as zero bits here. */ memzero((char *)int_types, sizeof(int_types)); memzero((char *)signed_int_types, sizeof(signed_int_types)); #if MICROSOFT_EXTENSIONS_ALLOWED memzero((char *)microsoft_sized_int_types, sizeof(microsoft_sized_int_types)); memzero((char *)microsoft_sized_signed_int_types, sizeof(microsoft_sized_signed_int_types)); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if FIXED_POINT_ALLOWED memzero((char *)fixed_point_types, sizeof(fixed_point_types)); #endif /* FIXED_POINT_ALLOWED */ memzero((char *)float_types, sizeof(float_types)); #if C99_IL_EXTENSIONS_SUPPORTED memzero((char *)complex_types, sizeof(complex_types)); memzero((char *)imaginary_types, sizeof(imaginary_types)); #endif /* C99_IL_EXTENSIONS_SUPPORTED */ { sizeof_t size = sizeof(a_type_ptr[(int)chk_last] [MAX_TRACKED_STRING_TYPE_LENGTH+1]); string_types = (a_type_ptr(*)[MAX_TRACKED_STRING_TYPE_LENGTH+1]) alloc_fe(size); memzero((char *)string_types, size_t_arg(size)); } #if NAMED_REGISTERS_ALLOWED memzero((char *)named_register_variables, sizeof(named_register_variables)); #endif /* NAMED_REGISTERS_ALLOWED */ il_wchar_t_type = NULL; il_char8_t_type = NULL; il_char16_t_type = NULL; il_char32_t_type = NULL; il_bool_type = NULL; il_error_type = il_unknown_type = il_void_type = NULL; il_reflection_type = NULL; il_standard_nullptr_type = NULL; il_managed_nullptr_type = NULL; il_strong_ordering_type = NULL; il_weak_ordering_type = NULL; il_partial_ordering_type = NULL; il_strong_equality_type = NULL; il_weak_equality_type = NULL; il_std_string_view = NULL; il_scalable_vector_count_type = NULL; il_modal_8bit_floating_point_type = NULL; il_float8e4m3_type = NULL; il_float8e5m2_type = NULL; il_source_location_impl_type = NULL; il_source_location_fields = {}; #if MICROSOFT_EXTENSIONS_ALLOWED idisposable_dispose_routine = NULL; object_finalize_routine = NULL; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ { sizeof_t size = sizeof(a_constant_ptr) * SIZE_SHAREABLE_CONSTANTS_TABLE; shareable_constants_table = (a_constant_ptr*)alloc_fe(size); memzero((char *)shareable_constants_table, size_t_arg(size)); } #if ORPHAN_PROCESSING_NEEDED /* Initialize the orphaned_file_scope_il_entries array to NULL pointers. */ memzero((char *)orphaned_file_scope_il_entries, sizeof(orphaned_file_scope_il_entries)); #endif /* ORPHAN_PROCESSING_NEEDED */ scheduled_routine_moves = NULL; scope_of_scheduled_routine_moves = NULL; scope_pointers_of_scheduled_routine_moves = NULL; routine_move_placeholders = NULL; n_scheduled_routine_moves = 0; reset_seq_cache(); il_alloc_trans_unit_init(); #if MODULE_ID_NEEDED module_id_scp = NULL; module_id_kind = iek_none; #endif /* MODULE_ID_NEEDED */ internal_type_array = NULL; n_internal_types = 0; strong_ordering_equal = NULL; strong_ordering_less = NULL; strong_ordering_greater = NULL; weak_ordering_equivalent = NULL; weak_ordering_less = NULL; weak_ordering_greater = NULL; partial_ordering_equivalent = NULL; partial_ordering_less = NULL; partial_ordering_greater = NULL; partial_ordering_unordered = NULL; strong_equality_equal = NULL; strong_equality_nonequal = NULL; weak_equality_equivalent = NULL; weak_equality_nonequivalent = NULL; } /* il_trans_unit_init */ void il_init(void) /* Initialize static variables related to the IL. This is done as a subroutine (rather than relying on static initialization) so that it can be redone to compile more than one source file in a single invocation of the front end. */ { last_routine_scope_variable_found = NULL; #if DEBUG num_shareable_constants = 0; num_func_shareable_constants = 0; num_used_shareable_constant_buckets = 0; num_searches_for_shareable_constants = 0; num_compares_for_shareable_constants = 0; num_get_based_type_calls = 0; num_based_type_fixups_allocated = 0; num_copy_remap_entries_allocated = 0; #endif /* DEBUG */ #if UPC_EXTENSIONS_ALLOWED max_upc_block_size = MAX_UPC_BLOCK_SIZE; #endif /* UPC_EXTENSIONS_ALLOWED */ curr_seq_number_lookup_entry = NULL; avail_copy_remap_entries = NULL; il_alloc_init(); } /* il_init */ #endif /* !STANDALONE_UTILITY_PROGRAM */ void rebuild_structures_on_il_read(void) /* Reconstruct any data structures that are built based on information in the IL. This routine is called after the IL has been read from a file. */ { build_seq_number_lookup_table(il_header.num_seq_number_lookup_entries); effective_primary_source_file = NULL; } /* rebuild_structures_on_il_read */ void il_reset(void) /* Reset any variables that contain state information that becomes invalid when the IL has been read back into memory. */ { reset_seq_cache(); okay_to_use_seq_number_lookup_table = FALSE; } /* il_reset */ /* Conditionally close the "edg" namespace. */ END_EDG_NAMESPACE