/* 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 */ /* exprutil.c -- Expression scanning utility routines. */ /* Header files common to all files. */ #include "fe_common.h" /* Header files used by files involved in expression processing. */ #include "expr_hdrs.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 "decl_inits.h" #include "preproc.h" #include "pch.h" #include "func_def.h" #include "il_walk.h" #include "interpret.h" #if DO_IL_LOWERING #include "lower_il.h" #endif /* DO_IL_LOWERING */ #include "statements.h" #if USE_X86_FUNCTION_MULTIVERSIONING #include "sys_predef.h" #endif /* USE_X86_FUNCTION_MULTIVERSIONING */ /* Conditionally open the "edg" namespace. */ BEGIN_EDG_NAMESPACE /* Declarations needed because of forward references: */ static a_boolean is_bit_field_expr(an_expr_node_ptr node); static a_boolean check_for_taking_the_address_of_a_bit_field( an_operand *operand, a_source_position *err_pos); static a_constant_ptr value_of_constant_var_glvalue_expr( an_expr_node_ptr node, a_boolean copy_for_reuse, a_variable_ptr *p_var); static an_expr_node_ptr conv_prvalue_expr_to_lvalue(an_expr_node_ptr node, a_boolean *converted, a_boolean see_if_possible, a_boolean gcc_lvalue, a_boolean ignore_casts, a_type_ptr *p_lvalue_type); static an_expr_node_ptr make_braced_init_expr_from_arg_list_elem( an_arg_list_elem_ptr alep); static void arg_list_elem_will_not_be_used_because_of_error( an_arg_list_elem_ptr alep); static void change_to_error_operand(an_operand *operand); #if MICROSOFT_EXTENSIONS_ALLOWED static a_boolean check_for_address_of_or_reference_to_initonly_field( an_operand *operand, a_source_position *err_pos, a_boolean reference_case); static a_boolean is_unmodifiable_initonly_field_operand( an_operand *operand, a_boolean *p_is_static_initonly_field); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ static void mark_init_component_as_permanently_allocated( an_init_component *icp); static void prep_generic_init_component_list(an_arg_list_elem_ptr list); static void make_template_param_object_operand(a_constant_ptr cp, an_operand *operand, a_boolean copy_constant); /* Information on references to symbols, held until the kind of reference to the symbol is known. */ STATIC_THREAD a_ref_entry_ptr avail_ref_entries; /* List of reference entries that have been freed and are available for reuse. */ #if SEQUENCING_DIAGNOSTICS_ENABLED STATIC_THREAD a_seq_pt_var_entry_ptr avail_seq_pt_var_entries; /* List of sequence point variable entries that have been freed and are available for reuse. */ STATIC_THREAD a_seq_pt_info_entry_ptr avail_sequence_info_entries; /* List of sequence point info entries that have been freed and are available for reuse. */ #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ STATIC_THREAD a_boolean sequencing_diagnostics_enabled; /* TRUE if sequencing diagnostics are enabled. */ STATIC_THREAD an_arg_operand_ptr avail_arg_operands; /* List of argument operand entries that have been freed and are available for reuse. */ STATIC_THREAD an_init_component_ptr avail_init_components; /* List of initializer list value entries that have been freed and are available for reuse. */ STATIC_THREAD a_dynamic_init_dtor_fixup_ptr avail_dynamic_init_dtor_fixups; /* List of dynamic init dtor fixup entries that have been freed and are available for reuse. */ #if DEBUG /* Counts of entries allocated, for debugging purposes. */ STATIC_THREAD unsigned long num_arg_operands_allocated, num_init_components_allocated, num_init_components_permanently_allocated, num_init_components_freed, num_expr_rescan_info_entries_allocated, num_ref_entries_allocated, num_dynamic_init_dtor_fixups_allocated; #if SEQUENCING_DIAGNOSTICS_ENABLED STATIC_THREAD unsigned long num_seq_pt_var_entries_allocated, num_sequence_info_entries_allocated; #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ void count_rescan_fs_expr_nodes(unsigned long *p_count) /* Increment *p_count (which is an estimate of the number of expression nodes allocated in file-scope memory during rescanning) if we are currently in a "rescanning context". The current context is considered a "rescanning context" if expr_stack->suppress_diagnostics is TRUE. */ { if (expr_stack != NULL && expr_stack->suppress_diagnostics) { *p_count += 1; } /* if */ } /* count_rescan_fs_expr_nodes */ #endif /* DEBUG */ #if SEQUENCING_DIAGNOSTICS_ENABLED static a_seq_pt_info_entry_ptr alloc_sequence_info_entry( an_expr_node_ptr expr) /* Allocate and return a sequence info entry that points to expr. */ { a_seq_pt_info_entry_ptr spiep; if (avail_sequence_info_entries != NULL) { /* Reuse a previously-freed entry. */ spiep = avail_sequence_info_entries; avail_sequence_info_entries = spiep->next; } else { /* Allocate a new entry. */ spiep = alloc_fe_of_type(a_seq_pt_info_entry); #if DEBUG num_sequence_info_entries_allocated++; #endif /* DEBUG */ } /* if */ spiep->next = NULL; spiep->expr = expr; spiep->independent_of_value_computation = FALSE; spiep->diagnostic_issued = FALSE; return spiep; } /* alloc_sequence_info_entry */ static void free_sequence_info_entry_list(a_seq_pt_info_entry_ptr spiep) /* Free the (possibly NULL) list of entries pointed to by spiep. */ { a_seq_pt_info_entry_ptr last; if (spiep != NULL) { /* Add the entries to the available list. */ for (last = spiep; last->next != NULL; last = last->next) {} last->next = avail_sequence_info_entries; avail_sequence_info_entries = spiep; } /* if */ } /* free_sequence_info_entry_list */ static a_seq_pt_var_entry_ptr alloc_seq_pt_var_entry(a_variable_ptr variable) /* Allocate and return a sequence point variable entry that points to variable. */ { a_seq_pt_var_entry_ptr spvep; if (avail_seq_pt_var_entries != NULL) { /* Reuse a previously-freed entry. */ spvep = avail_seq_pt_var_entries; avail_seq_pt_var_entries = spvep->next; } else { /* Allocate a new entry. */ spvep = alloc_fe_of_type(a_seq_pt_var_entry); #if DEBUG num_seq_pt_var_entries_allocated++; #endif /* DEBUG */ } /* if */ spvep->next = NULL; spvep->variable = variable; spvep->side_effects = NULL; spvep->uses = NULL; return spvep; } /* alloc_seq_pt_var_entry */ static a_seq_pt_var_entry_ptr seq_pt_entry_for_var( a_seq_pt_var_entry_ptr *list, a_variable_ptr variable) /* If a sequence point variable entry for variable exists on list, return it, otherwise allocate a new entry, queue it on the list and return it. The list is kept sorted by the variable's name. */ { a_seq_pt_var_entry_ptr spvep, prev = NULL; for (spvep = *list; spvep != NULL; spvep = spvep->next) { if (spvep->variable == variable || strcmp(spvep->variable->source_corresp.name, variable->source_corresp.name) > 0) break; prev = spvep; } /* for */ if (spvep == NULL || spvep->variable != variable) { /* Not on the list. */ spvep = alloc_seq_pt_var_entry(variable); if (prev == NULL) { spvep->next = *list; *list = spvep; } else { spvep->next = prev->next; prev->next = spvep; } /* if */ } /* if */ return spvep; } /* seq_pt_entry_for_var */ static void free_seq_pt_entry_for_var(a_seq_pt_var_entry_ptr spvep) /* Free the entry pointed to by spvep. */ { /* Add the entry to the available list. */ if (spvep->side_effects != NULL) { free_sequence_info_entry_list(spvep->side_effects); } /* if */ if (spvep->uses != NULL) { free_sequence_info_entry_list(spvep->uses); } /* if */ spvep->next = avail_seq_pt_var_entries; avail_seq_pt_var_entries = spvep; } /* free_seq_pt_entry_for_var */ static void free_seq_pt_var_entry_list(a_seq_pt_var_entry_ptr spvep) /* Free all entries on the list headed by spvep (which may be NULL). */ { a_seq_pt_var_entry_ptr spvep_next; while (spvep != NULL) { spvep_next = spvep->next; free_seq_pt_entry_for_var(spvep); spvep = spvep_next; } /* while */ } /* free_seq_pt_var_entry_list */ #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ static a_ref_entry_ptr alloc_ref_entry(a_symbol_ptr sym_ptr, a_source_position *pos) /* Allocate a reference entry and return a pointer to it. The information provided (kind of expression, symbol pointer, and location of reference) is placed in the entry. At this point the kind of reference is the generic SRK_REFERENCE. An entry of this kind is used to hold information on a single reference to a symbol in an expression. The information is held, rather than recorded immediately, because the kind of reference may be revised as more of the expression is scanned. The entry is put on the reference list for the current expression, headed by curr_expr_ref_entries. */ { a_ref_entry_ptr rep; if (avail_ref_entries != NULL) { /* Reuse a previously-freed entry. */ rep = avail_ref_entries; avail_ref_entries = rep->next; } else { /* Allocate a new entry. */ rep = (a_ref_entry_ptr)alloc_fe(sizeof(a_ref_entry)); #if DEBUG num_ref_entries_allocated++; #endif /* DEBUG */ } /* if */ rep->kind = SRK_REFERENCE; if (expr_stack->is_default_arg_expression) { rep->kind |= SRK_DEFAULT_ARG_EXPR; } /* if */ rep->already_recorded = FALSE; #if CHECKING rep->freed = FALSE; #endif /* CHECKING */ rep->symbol = sym_ptr; rep->specific_il_entry = NULL; copy_source_position(*pos, rep->position); rep->next = NULL; rep->next_operand_ref = NULL; if (sym_ptr->is_pack_element) { /* Remember the current associated IL entry for a pack element. */ rep->specific_il_entry = source_corresp_entry_for_symbol(sym_ptr); } /* if */ /* Put the entry on the list of entries for the current expression. The list is dumped when flush_ref_entries_list is called. The entry is put at the end of the list to preserve source order. */ if (curr_expr_ref_entries == NULL) { curr_expr_ref_entries = rep; } else { a_ref_entry_ptr last_rep; for (last_rep = curr_expr_ref_entries; last_rep->next != NULL; last_rep = last_rep->next) { check_assertion(last_rep != rep); } /* for */ last_rep->next = rep; } /* if */ return rep; } /* alloc_ref_entry */ a_ref_entry_ptr copy_ref_entry_list(a_ref_entry_ptr ref_list) /* Make a copy of a list of reference entries, and return a pointer to the copied list. The list of entries is for a single operand, and is linked on the next_operand_ref field. The new entries are placed on the reference list for the current expression, headed by curr_expr_ref_entries. */ { a_ref_entry_ptr copy_list = NULL, copy_list_end = NULL; for (; ref_list != NULL; ref_list = ref_list->next_operand_ref) { a_ref_entry_ptr new_ref = alloc_ref_entry(ref_list->symbol, &ref_list->position); a_ref_entry_ptr new_next = new_ref->next; *new_ref = *ref_list; new_ref->next = new_next; new_ref->next_operand_ref = NULL; if (copy_list == NULL) { copy_list = new_ref; } else { copy_list_end->next_operand_ref = new_ref; /*lint !e413*/ } /* if */ copy_list_end = new_ref; } /* for */ return copy_list; } /* copy_ref_entry_list */ static void free_ref_entry(a_ref_entry_ptr rep) /* Free the reference entry pointed to by rep. */ { #if CHECKING check_assertion_str(!rep->freed, "ref entry freed twice"); rep->freed = TRUE; #endif /* CHECKING */ /* Add the entry to the available list. */ rep->next = avail_ref_entries; avail_ref_entries = rep; } /* free_ref_entry */ static void record_reference(a_ref_entry_ptr rep) /* Record the symbol reference described by the reference entry rep. */ { /* Do not record the reference if it has already been recorded. */ if (!rep->already_recorded) { record_symbol_reference_full(rep->kind, rep->symbol, &rep->position, /*update_il_entry=*/TRUE, rep->specific_il_entry); rep->already_recorded = TRUE; } /* if */ } /* record_reference */ static void record_and_free_ref_entry(a_ref_entry_ptr rep) /* Record the reference indicated in the reference entry rep, and free the entry. */ { record_reference(rep); free_ref_entry(rep); } /* record_and_free_ref_entry */ static a_boolean on_operand_ref_list(a_ref_entry_ptr rep, a_ref_entry_ptr list) /* Return TRUE if the reference entry "rep" appears on the list of reference entries headed by "list". The list considered is the one linked on the next_operand_ref field, i.e., the list attached to an operand. */ { a_boolean on_list = FALSE; for (; list != NULL; list = list->next_operand_ref) { if (rep == list) { /* The entry is on the list. */ on_list = TRUE; break; } /* if */ } /* for */ return on_list; } /* on_operand_ref_list */ void record_operand_ref_entries(an_operand *operand) /* Record the reference entries associated with the given operand. (Used for error recovery purposes to ensure that, e.g., symbols are marked as referenced even if the associated expression is discarded.) */ { a_ref_entry_ptr rep = operand->ref_entries_list; for (; rep != NULL; rep = rep->next_operand_ref) { record_reference(rep); } /* for */ } /* record_operand_ref_entries */ void flush_ref_entries_except(a_ref_entry_ptr keep_list1, a_ref_entry_ptr keep_list2) /* Look for reference entries on the curr_expr_ref_entries list that are not also on either of the keep_list1 or keep_list2 lists. For each such entry, record the indicated reference and free the entry. This is used at the ends of expressions to commit the references that are not being passed up to the caller and therefore cannot be modified further. */ { a_ref_entry_ptr rep, last_rep, next_rep; rep = curr_expr_ref_entries; curr_expr_ref_entries = NULL; last_rep = NULL; for (; rep != NULL; rep = next_rep) { next_rep = rep->next; if (on_operand_ref_list(rep, keep_list1) || on_operand_ref_list(rep, keep_list2)) { /* This entry appears on a list, so keep it on the global list. */ if (curr_expr_ref_entries == NULL) { curr_expr_ref_entries = rep; } else { /* coverity[var_deref_op] */ last_rep->next = rep; /*lint !e413*/ } /* if */ last_rep = rep; rep->next = NULL; } else { /* This entry does not appear on a list, so record it and free it. */ record_and_free_ref_entry(rep); } /* if */ } /* for */ } /* flush_ref_entries_except */ static void flush_ref_entries_list(void) /* If there are any entries on the list of reference entries for the current expression, record and free them now. */ { a_ref_entry_ptr rep, list = curr_expr_ref_entries; curr_expr_ref_entries = NULL; while (list != NULL) { rep = list; list = rep->next; record_and_free_ref_entry(rep); } /* while */ } /* flush_ref_entries_list */ a_ref_entry_ptr ref_entry(a_symbol_ptr sym_ptr, a_source_position *source_position) /* Allocate a reference entry for a reference to the symbol sym_ptr at position source_position, put the entry on the list of entries for the current expression, and return a pointer to it. This is needed when the kind of reference is not known yet because it can be affected by context. When the proper reference kind has been determined, the entry will be updated, and the reference will be recorded when the entry is freed. If the proper kind of reference is known right away, it is recorded right away and no entry is created; NULL is returned. */ { a_ref_entry_ptr rep; a_boolean ref_kind_can_be_affected_by_context = FALSE; a_boolean evaluated = curr_expr_is_potentially_evaluated(); a_symbol_ptr fund_sym = fundamental_symbol_of(sym_ptr); if (sym_ptr->ambiguous) { /* Do not record references to ambiguous symbols, since we don't know which symbol is referenced. */ rep = NULL; } else if (expr_stack != NULL && expr_stack->suppress_diagnostics) { /* SFINAE processing and the evaluation of certain pseudo-functions (like __is_trivially_assignable) don't really "reference" any symbols. */ rep = NULL; if (is_simple_function_symbol(fund_sym) && fund_sym->variant.routine.ptr->is_deleted) { record_suppressed_error(); } /* if */ } else { /* For only certain kinds of symbols can the kind of reference be affected by context: for example, variables can have SRK_USE, SRK_MODIFICATION, and SRK_ADDRESS_TAKEN references, but types can only have SRK_REFERENCE references. */ switch (fund_sym->kind) { case sk_constant: /* Constant (enumerator). */ case sk_variable: /* Variable or parameter. */ case sk_field: /* Nonstatic data member of a class. */ case sk_static_data_member: /* Static data member of a class. */ case sk_member_function: /* Member function of a class. */ case sk_routine: /* Nonmember function. */ ref_kind_can_be_affected_by_context = TRUE; break; case sk_overloaded_function: /* Overloaded function (member or not). */ unexpected_condition_str("ref_entry: overloaded function"); break; #if MICROSOFT_EXTENSIONS_ALLOWED case sk_property_set: unexpected_condition_str("ref_entry: property set"); break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ default:; ref_kind_can_be_affected_by_context = FALSE; break; } /* switch */ /* References in not-evaluated expressions are always plain references, since they don't use or affect the values of variables. */ if (!ref_kind_can_be_affected_by_context || !evaluated) { /* The kind of reference is independent of context, so record it right away and do not build an entry. */ record_symbol_reference(SRK_REFERENCE, fund_sym, source_position, /*update_il_entry=*/evaluated); rep = NULL; } else { /* The kind of reference can be affected by context, so build an entry for it. */ rep = alloc_ref_entry(fund_sym, source_position); } /* if */ } /* if */ return rep; } /* ref_entry */ static void f_check_address_taken_ref(a_ref_entry_ptr rep) /* The reference entry pointed to by rep includes an address-taken reference. Check to see if the symbol in the reference can have its address taken, and if not issue an error. */ { a_symbol_ptr sym = rep->symbol; /* Note that this routine is not the right place to set the address_taken flag in routines, because not all references that are address-taken initially stay that way. See the comment for variables below. */ /* The only possible error cases are variables, and only register variables at that. Functions can always have their addresses taken. Also static data members (they cannot be "register"). */ if (sym->kind == (a_symbol_kind)sk_variable) { a_variable_ptr var = sym->variant.variable.ptr; if (C_mode() && (var->storage_class == (a_storage_class)sc_register #if NAMED_REGISTERS_ALLOWED || var->has_named_register_storage_class #endif /* NAMED_REGISTERS_ALLOWED */ )) { /* Cannot take the address of a register variable in C (this includes variables with named-register storage class in Embedded C). For ordinary register variables, this is allowed in C++, and is allowed (with a warning) in C (except in strict error mode). For named registers, a strict error is always issued. */ if ( #if NAMED_REGISTERS_ALLOWED !var->has_named_register_storage_class && #endif /* NAMED_REGISTERS_ALLOWED */ (SVR4_C_mode || strict_ansi_error_severity != es_error)) { expr_pos_warning(ec_address_of_register_variable, &rep->position); } else { expr_pos_error(ec_address_of_register_variable, &rep->position); /* Turn the reference into an error reference so that the error will be issued only once. */ rep->kind = (rep->kind & ~SRK_ALL_REFERENCES) | SRK_ERROR; rep->already_recorded = FALSE; } /* if */ } /* if */ /* Indicate that the address of the variable has been taken. Setting the flag here means it is set even for cases where an address is used at some intermediate step but doesn't escape, e.g., a[2] = 1; // address_taken on "a" but a simple interpretation of address_taken seems to be what people prefer. Note that the flag is also set when the reference is recorded later, but only if the address-taken reference survives to that point, so the setting there is more discriminating. One can remove the assignment here to get the other interpretation. If one does so, one should consider whether one wants register variables and parameters treated in some special way (e.g., one might want to set the address_taken flag here anyway for those), since the address_taken flag might be used to decide whether to allocate storage for those variables. */ set_variable_address_taken(var); } else if (sym->kind == (a_symbol_kind)sk_static_data_member) { /* For simple interpretation of address_taken, set address_taken on static data members here. This can be removed. See comments above. */ set_variable_address_taken(sym->variant.static_data_member.variable); } else if (sym->kind == (a_symbol_kind)sk_field) { /* For anonymous union fields, if the field belongs to an unnamed anonymous union variable, set the address_taken field on that variable. Walk up the list of anonymous_parent_objects until we get to a variable, if any. Only in C++. This can be removed. See comments above. */ a_symbol_ptr sym_apo = sym->variant.field.anonymous_parent_object; while (sym_apo != NULL) { if (sym_apo->kind == (a_symbol_kind)sk_variable) { set_variable_address_taken(sym_apo->variant.variable.ptr); break; } else if (sym_apo->kind != (a_symbol_kind)sk_field) { break; } /* if */ sym_apo = sym_apo->variant.field.anonymous_parent_object; } /* while */ } /* if */ } /* f_check_address_taken_ref */ /* If the reference entry pointed to by rep includes an address-taken reference, check to see if the symbol in the reference can have its address taken, and if not issue an error. */ #define check_address_taken_ref(rep) \ { if ((rep)->kind & SRK_ADDRESS_TAKEN) f_check_address_taken_ref(rep); } void change_ref_kinds(a_ref_entry_ptr ref_list, a_symbol_reference_kind new_kind) /* Change the kind-of-reference field to new_kind in each of the reference entries on the list ref_list. The list is linked by the next_operand_ref field. */ { a_ref_entry_ptr rep; a_symbol_reference_kind old_kind; /* Go through the list of references and change the reference kinds. */ for (rep = ref_list; rep != NULL; rep = rep->next_operand_ref) { old_kind = rep->kind; if (old_kind & SRK_ERROR) { /* An error reference is never changed to something else. */ } else { /* For some cases, the old kind of reference is put out before the new kind is set. That's necessary, for example, in void f(int &); int j; void m () { f(j = 1); // Modification gets replaced by address taken } One really wants both kinds of references. A similar case is (j = k)++; One wants to record the modification separately and before the use of the value to avoid a diagnostic about using the variable before it is set. */ if ((old_kind & SRK_MODIFICATION) && (new_kind & (SRK_ADDRESS_TAKEN | SRK_USE))) { record_reference(rep); } /* if */ if ((new_kind & SRK_PROTO_INST_REF) != 0 && is_local_symbol(rep->symbol)) { /* Prototype instantiations should not mark non-local scope entities as referenced and that is what SRK_PROTO_INST_REF is for. However, for local entities, we do want to mark them since otherwise we may produce spurious warnings about unreferenced local variables. */ if (!in_unevaluated_expr_context()) { rep->kind |= SRK_REFERENCE; } /* if */ record_reference(rep); } /* if */ /* Set the new reference kind. Turn off all old bits, then turn on new bits. SRK_REFERENCE remains set in all cases. */ rep->kind = (old_kind & ~SRK_ALL_REFERENCES) | new_kind; rep->already_recorded = FALSE; /* If the reference kinds include address-taken, check for errors related to that. */ check_address_taken_ref(rep); } /* if */ } /* for */ } /* change_ref_kinds */ void change_refs_to_error(a_ref_entry_ptr ref_list) /* Change the reference entries on the list ref_list to error references. The list is linked by the next_operand_ref field. */ { change_ref_kinds(ref_list, SRK_ERROR); } /* change_refs_to_error */ void change_operand_refs_to_error(an_operand *operand) /* Change the reference kind in any references attached to operand to SRK_ERROR. */ { change_refs_to_error(operand->ref_entries_list); /* Error entries cannot be changed to anything else, so there's no point in keeping them attached to the operand. */ operand->ref_entries_list = NULL; } /* change_operand_refs_to_error */ void change_arg_list_refs_to_error(an_arg_list_elem_ptr arg_list) /* Change the references on each operand in the list of arguments headed by arg_list to error references. */ { an_arg_list_elem_ptr alep; for (alep = arg_list; alep != NULL; alep = next_elem(alep)) { if (is_expression_component(alep)) { change_operand_refs_to_error(operand_of_arg_list_elem(alep)); } else if (is_braced_init_component(alep)) { change_arg_list_refs_to_error(alep->variant.braced.list); } /* if */ } /* for */ } /* change_arg_list_refs_to_error */ void change_some_ref_kinds(a_ref_entry_ptr ref_list, a_symbol_reference_kind old_kind, a_symbol_reference_kind new_kind) /* Change the kind-of-reference field to "new_kind" in each of the reference entries on the list ref_list that currently has the kind "old_kind". The list is linked by the next_operand_ref field. */ { a_ref_entry_ptr rep; a_boolean changed; for (rep = ref_list; rep != NULL; rep = rep->next_operand_ref) { changed = FALSE; if (old_kind == SRK_REFERENCE) { /* Changing a generic reference (SRK_REFERENCE alone) to another kind of reference. Check to see if the rep entry is generic. */ if ((rep->kind & SRK_ALL_REFERENCES) == 0) { /* Yes. Change the reference kind. */ rep->kind |= new_kind; rep->already_recorded = FALSE; changed = TRUE; } /* if */ } else if ((rep->kind & old_kind) != 0) { /* Changing a specific reference to another kind of reference. Turn off the old bits, then turn on the new bits. SRK_REFERENCE will stay set. */ rep->kind = (rep->kind & ~SRK_ALL_REFERENCES) | new_kind; rep->already_recorded = FALSE; changed = TRUE; } /* if */ /* If the reference kinds include address-taken, check for errors related to that. */ if (changed) check_address_taken_ref(rep); } /* for */ } /* change_some_ref_kinds */ void record_operand_modification_refs(an_operand *operand) /* Record any modification references indicated on the references list for *operand. This is used at potential sequence points. The reference entries are left on the list but are marked as having been already recorded. */ { a_ref_entry_ptr rep; for (rep = operand->ref_entries_list; rep != NULL; rep = rep->next_operand_ref) { if (rep->kind & SRK_MODIFICATION) { record_reference(rep); } /* if */ } /* for */ } /* record_operand_modification_refs */ void detach_ref_entries_from_curr_expr(an_operand *operand) /* The given operand is going to be saved off to the side and brought back later (e.g., by being put into an initializer cache). Detach its reference entries from the current expression so they will not be closed out when the expression terminates. */ { a_ref_entry_ptr rep, rep2, prev_rep2; for (rep = operand->ref_entries_list; rep != NULL; rep = rep->next_operand_ref) { for (prev_rep2 = NULL, rep2 = curr_expr_ref_entries; rep2 != NULL; prev_rep2 = rep2, rep2 = rep2->next) { if (rep == rep2) { /* Remove this entry from the curr_expr_ref_entries list. */ if (prev_rep2 == NULL) { curr_expr_ref_entries = rep2->next; } else { prev_rep2->next = rep2->next; } /* if */ rep2->next = NULL; goto outer_loop; } /* if */ } /* for */ check_assertion_or_expect_error_str( !is_an_lvalue(operand), "detach_ref_entries_from_curr_expr: not found"); operand->ref_entries_list = NULL; outer_loop:; } /* for */ /* Discard any ref entries saved so we can turn an rvalue back into an lvalue in gcc mode. We're going to assume they won't be needed in the contexts where bundling is done. */ operand->saved_ref_entries_list = NULL; } /* detach_ref_entries_from_curr_expr */ void reattach_ref_entries_to_curr_expr(an_operand *operand) /* The given operand was saved off to the side (e.g., by being put into an initializer cache). Now, it's being brought back for the rest of its processing. If it has attached reference entries, add them to the list for the current expression. */ { a_ref_entry_ptr first_rep = operand->ref_entries_list; if (first_rep != NULL) { a_ref_entry_ptr rep; /* Find the end of the list linked on next_operand_ref, and on the way relink the entries on that list with the "next" pointer as well. */ for (rep = first_rep; rep->next_operand_ref != NULL; rep = rep->next_operand_ref) { check_assertion(rep != curr_expr_ref_entries); rep->next = rep->next_operand_ref; } /* for */ rep->next = curr_expr_ref_entries; curr_expr_ref_entries = first_rep; } /* if */ } /* reattach_ref_entries_to_curr_expr */ an_arg_operand_ptr alloc_arg_operand(void) /* Allocate an argument operand entry and return a pointer to it. Such entries are used to hold arguments of function calls. */ { an_arg_operand_ptr aop; if (avail_arg_operands != NULL) { /* Reuse a previously-freed entry. */ aop = avail_arg_operands; avail_arg_operands = aop->next; } else { /* Allocate a new entry. */ aop = (an_arg_operand_ptr)alloc_fe(sizeof(an_arg_operand)); #if DEBUG num_arg_operands_allocated++; #endif /* DEBUG */ } /* if */ aop->next = NULL; clear_operand((an_operand_kind)ok_error, &aop->operand); return aop; } /* alloc_arg_operand */ an_arg_operand_ptr arg_operand_for_constant(a_constant_ptr cp) /* Allocate and return an entry of type an_arg_operand initialized with the given constant. */ { an_arg_operand_ptr aop = alloc_arg_operand(); make_constant_operand(cp, &aop->operand); return aop; } /* arg_operand_for_constant */ /*lint -ecall(523,*free_attachments_to_operand)*/ void free_attachments_to_operand(ARG_UNUSED an_operand *operand) /* Free any dynamically-allocated attachments to the indicated operand. The operand will not be used further. */ { /* This is a hook for future expansion. */ /* The init-components attached to an ok_braced_init_list entry are permanently allocated to avoid memory management issues. Likewise the subscripts attached to an ok_property_ref. */ } /* free_attachments_to_operand */ void free_arg_operand_list(an_arg_operand_ptr aop) /* Free the list of argument operands pointed to by aop. */ { an_arg_operand_ptr aop_next; for (; aop != NULL; aop = aop_next) { aop_next = aop->next; /* Free any dynamically-allocated attachments to the operand. */ free_attachments_to_operand(&aop->operand); /* Add the entry to the available list. */ aop->next = avail_arg_operands; avail_arg_operands = aop; } /* for */ } /* free_arg_operand_list */ static void set_init_component_kind(an_init_component_ptr icp, an_init_component_kind kind) /* Set the kind of the init component entry to "kind", and set the associated variant fields to default values. */ { icp->kind = kind; switch (icp->kind) { case ick_expression: icp->variant.expr.arg_op = alloc_arg_operand(); icp->variant.expr.lifetime = NULL; break; case ick_braced: icp->variant.braced.list = NULL; icp->variant.braced.start_pos = null_source_position; icp->variant.braced.end_pos = null_source_position; break; case ick_designator: icp->variant.designator.field_name = NULL; icp->variant.designator.element_index = NULL; icp->variant.designator.last_element_index = NULL; icp->variant.designator.position = null_source_position; icp->variant.designator.resolved_field = NULL; break; case ick_continued: icp->variant.continuation.state = NULL; break; default: unexpected_condition_str("set_init_component_kind: bad kind"); } /* switch */ } /* set_init_component_kind */ an_init_component_ptr alloc_init_component(an_init_component_kind kind) /* Allocate an entry used to describe a value in an initializer list, set its kind to "kind" and its fields to default values, and return a pointer to it. */ { an_init_component_ptr icp; if (avail_init_components != NULL) { /* Reuse a previously-freed entry. */ icp = avail_init_components; avail_init_components = icp->next; #if DEBUG num_init_components_freed--; #endif /* DEBUG */ } else { /* Allocate a new entry. */ icp = alloc_fe_of_type(an_init_component); #if DEBUG num_init_components_allocated++; #endif /* DEBUG */ } /* if */ icp->next = NULL; icp->bundled = FALSE; icp->detached_ref_entries = FALSE; icp->contains_designator = FALSE; icp->check_narrowing = FALSE; icp->braced_init_in_parentheses = FALSE; icp->permanently_allocated = FALSE; #if CHECKING icp->on_free_list = FALSE; #endif /* CHECKING */ icp->constant_expr_ruled_out = FALSE; icp->consteval_function_designator_seen = FALSE; icp->preserved_deduced_pack = FALSE; icp->direct_init_designator = FALSE; icp->pack_expansion_descr = NULL; set_init_component_kind(icp, kind); return icp; } /* alloc_init_component */ an_arg_list_elem_ptr alloc_arg_list_elem_for_operand(an_operand *operand) /* Allocate an init component/arg list element containing the given operand. */ { an_init_component_ptr icp = alloc_init_component(ick_expression); /* If the operand is a braced-init-list, the copy should be handled explicitly by the caller. At this level, we can't deal with the allocation/freeing issues without modifying the source operand or deep copying. */ check_assertion(!is_braced_init_list_operand(operand)); copy_operand(operand, operand_of_arg_list_elem(icp)); return icp; } /* alloc_arg_list_elem_for_operand */ void free_init_component(an_init_component_ptr icp) /* Free the initializer component entry pointed to by icp. If it has a subtree of entries, free those as well. */ { check_assertion(icp != NULL); if (icp->permanently_allocated) { /* Don't free a permanently-allocated entry. */ goto end_of_routine; } /* if */ #if CHECKING check_assertion_str(!icp->on_free_list, "free_init_component: entry freed twice"); icp->on_free_list = TRUE; #endif /* CHECKING */ switch (icp->kind) { case ick_expression: check_assertion(icp->variant.expr.arg_op != NULL && icp->variant.expr.arg_op->next == NULL); free_arg_operand_list(icp->variant.expr.arg_op); icp->variant.expr.arg_op = NULL; break; case ick_braced: free_init_component_list(icp->variant.braced.list); icp->variant.braced.list = NULL; break; case ick_designator: icp->variant.designator.field_name = NULL; icp->variant.designator.element_index = NULL; icp->variant.designator.last_element_index = NULL; break; case ick_continued: icp->variant.continuation.state = NULL; break; default: unexpected_condition_str("free_init_component: bad entry kind"); } /* switch */ icp->next = avail_init_components; avail_init_components = icp; #if DEBUG num_init_components_freed++; #endif /* DEBUG */ end_of_routine:; } /* free_init_component */ void free_init_component_list(an_init_component_ptr icp) /* Free the initializer component entry list pointed to by icp. */ { an_init_component_ptr next_icp; for (; icp != NULL; icp = next_icp) { next_icp = icp->next; free_init_component(icp); } /* for */ } /* free_init_component_list */ a_source_position *init_component_pos(an_init_component_ptr icp) /* Return the position of the given init component. */ { a_source_position *result = NULL; switch (icp->kind) { case ick_braced: result = &icp->variant.braced.start_pos; break; case ick_expression: result = &icp->variant.expr.arg_op->operand.position; break; case ick_designator: result = &icp->variant.designator.position; break; default: unexpected_condition(); } /* switch */ return result; } /* init_component_pos */ #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position *init_component_end_pos(an_init_component_ptr icp) /* Return the end position of the given init component: It can represent a braced list or an expression, but not a designator. */ { check_assertion(!is_designator_component(icp)); return icp->kind == (an_init_component_kind)ick_braced ? &icp->variant.braced.end_pos : &icp->variant.expr.arg_op->operand.end_position; } /* init_component_end_pos */ #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ a_boolean is_error_component(an_init_component_ptr icp) /* If the given initialization component is an error expression, return TRUE. Otherwise, return FALSE. */ { a_boolean result = FALSE; if (is_expression_component(icp)) { result = is_error_operand(operand_of_arg_list_elem(icp)); } /* if */ return result; } /* is_error_component */ a_boolean is_string_literal_component(an_init_component_ptr icp, a_constant_ptr *p_con) /* If the given initialization component is a string literal return TRUE and return the constant representing that literal in *p_con. Otherwise, return FALSE. */ { a_boolean result = FALSE; if (is_expression_component(icp)) { an_operand *operand = operand_of_arg_list_elem(icp); if (operand_is_string_literal(operand)) { /* The operand is a simple string literal (e.g., it has not been cast). */ result = TRUE; *p_con = &operand->variant.constant; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (microsoft_mode) { /* In Microsoft mode, a string literal cast to a matching character pointer type is treated as a literal too. */ result = operand_is_cast_string_literal(operand, p_con); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ } /* if */ return result; } /* is_string_literal_component */ a_boolean is_parenthesized_component(an_init_component_ptr icp) /* If the given initialization component is a parenthesized expression, return TRUE. Otherwise, return FALSE. */ { a_boolean result = FALSE; if (is_expression_component(icp)) { result = operand_of_arg_list_elem(icp)->is_parenthesized; } /* if */ return result; } /* is_parenthesized_component */ a_boolean is_pack_expansion_component(an_init_component_ptr icp) /* If the given initialization component is a pack expansion, return TRUE. Otherwise, return FALSE. */ { a_boolean result = (icp->pack_expansion_descr != NULL); if (!result && pack_expansion_stack != NULL && pack_expansion_stack->is_suppression && is_expression_component(icp)) { /* When we've suppressed pack expansions, the pack expansion description will be NULL. To answer whether this is a pack expansion component, we need to dig deeper. */ an_operand_ptr op = operand_of_arg_list_elem(icp); if (is_template_param_constant_operand(op) && op->variant.constant.variant.template_param.is_pack) { result = TRUE; } /* if */ } /* if */ return result; } /* is_pack_expansion_component */ a_boolean any_pack_expansion_components_in_list(an_init_component_ptr icp) /* If any of the given initialization components in this list are a pack expansion, return TRUE. Otherwise, return FALSE. */ { a_boolean result = FALSE; for (; icp != NULL; icp = icp->next) { if (is_pack_expansion_component(icp)) { result = TRUE; break; } /* if */ } /* for */ return result; } /* any_pack_expansion_components_in_list */ #if DEBUG void db_init_component(an_init_component_ptr icp) /* Display an init component for debugging purposes. */ { if (icp == NULL) { (void)fprintf(f_debug, "\n"); goto end_of_routine; } /* if */ switch (icp->kind) { case ick_expression: db_operand(&icp->variant.expr.arg_op->operand); break; case ick_braced: (void)fprintf(f_debug, "(%lu,%lu) {\n", (unsigned long)icp->variant.braced.start_pos.seq, (unsigned long)icp->variant.braced.start_pos.column); { an_init_component_ptr elem_icp; for (elem_icp = icp->variant.braced.list; elem_icp != NULL; elem_icp = elem_icp->next) { db_init_component(elem_icp); } /* for */ } (void)fprintf(f_debug, "} (%lu,%lu)\n", (unsigned long)icp->variant.braced.end_pos.seq, (unsigned long)icp->variant.braced.end_pos.column); break; case ick_designator: if (icp->variant.designator.field_name != NULL) { (void)fprintf(f_debug, ".%s\n", icp->variant.designator.field_name ->identifier); } else { (void)fprintf(f_debug, "["); db_constant(icp->variant.designator.element_index); if (icp->variant.designator.element_index != icp->variant.designator.last_element_index) { (void)fprintf(f_debug, " ... "); db_constant(icp->variant.designator.last_element_index); } /* if */ (void)fprintf(f_debug, "]\n"); } /* if */ break; case ick_continued: (void)fprintf(f_debug, "Pending initializer elements...\n"); break; default: (void)fprintf(f_debug, "Bad init component kind\n"); break; } /* switch */ if (icp->pack_expansion_descr != NULL) { (void)fprintf(f_debug, "...\n"); } /* if */ end_of_routine:; } /* db_init_component */ static void db_icp(an_init_component_ptr icp) /* Shorthand for db_init_component. */ { db_init_component(icp); } /* db_icp */ void db_icp_list(an_init_component_ptr icp) /* Output a list of initializer components for debugging purposes. */ { long n = 0; while (icp != NULL) { (void)fprintf(f_debug, "#%ld:\n", ++n); db_icp(icp); icp = icp->next; } /* if */ } /* db_icp_list */ #endif /* DEBUG */ void conv_braced_init_component_to_error_expression(an_arg_list_elem_ptr alep) /* Convert a braced-init-list component to an error expression component. */ { a_source_position start_pos = alep->variant.braced.start_pos; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_pos = alep->variant.braced.end_pos; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ an_operand *operand; arg_list_elem_will_not_be_used_because_of_error(alep); free_init_component_list(alep->variant.braced.list); set_init_component_kind(alep, (an_init_component_kind)ick_expression); /* Note that the "next" pointer is preserved. */ operand = operand_of_arg_list_elem(alep); make_error_operand(operand); operand->position = start_pos; #if EXTRA_SOURCE_POSITIONS_IN_IL operand->end_position = end_pos; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* conv_braced_init_component_to_error_expression */ void check_arg_list_elem_is_expression(an_arg_list_elem_ptr alep) /* Check that the given member of an argument list is an expression. If it's not (e.g., if it's a brace-enclosed list), issue an error and change it to an error expression. */ { if (is_expression_component(alep)) { /* Okay. */ } else if (is_braced_init_component(alep)) { expr_pos_error(ec_braced_init_list_not_allowed, init_component_pos(alep)); conv_braced_init_component_to_error_expression(alep); } else { unexpected_condition(); } /* if */ } /* check_arg_list_elem_is_expression */ void clear_initializer_cache(an_initializer_cache *cache) /* Set the fields of an initializer cache to default values. */ { cache->first_init = NULL; cache->last_init = NULL; } /* clear_initializer_cache */ void flush_initializer_cache(an_initializer_cache *cache) /* Empty out an existing initializer cache, discarding any expressions or braced-init-lists currently in it. */ { free_init_component_list(cache->first_init); clear_initializer_cache(cache); } /* flush_initializer_cache */ void add_init_component_to_initializer_cache(an_init_component_ptr icp, a_boolean to_front, an_initializer_cache *cache) /* Add the indicated initializer list component to the end of the queue of initializer components in the indicated initializer cache (or the front if to_front is TRUE). When to_front is TRUE, icp may be a list of elements. */ { if (to_front) { /* Add to the front of the queue. */ an_init_component_ptr prev = cache->first_init; cache->first_init = icp; while (icp->next != NULL) icp = icp->next; if (cache->last_init == NULL) { cache->last_init = icp; } else { append_elem(icp, prev); } /* if */ } else { /* Add to the end of the queue. */ if (cache->first_init == NULL) { cache->first_init = icp; } else { append_elem(cache->last_init, icp); } /* if */ cache->last_init = icp; } /* if */ } /* add_init_component_to_initializer_cache */ void add_operand_to_initializer_cache(an_operand *operand, a_boolean to_front, a_boolean bundle, an_initializer_cache *cache) /* Add the indicated operand to the end of the queue of initializer components in the indicated initializer cache (or the front if to_front is TRUE). bundle is TRUE if the expression should be "bundled", meaning packaged with related information so it can be saved off to the side (e.g., in an initializer cache) for later restoration and further processing. */ { an_init_component_ptr icp = alloc_arg_list_elem_for_operand(operand); if (bundle) { if (expr_stack->lifetime != NULL) { /* Preserve the lifetime associated with the expression. This is related to what scan_expr_as_init_component does, but in this case the operand has already been scanned, or taken out of a cache and its lifetime restored, so we're just saving here, not wrapping. */ check_assertion(curr_object_lifetime == expr_stack->lifetime); icp->variant.expr.lifetime = expr_stack->lifetime; curr_object_lifetime = curr_object_lifetime->parent_lifetime; expr_stack->lifetime = NULL; detach_from_object_lifetime_tree(icp->variant.expr.lifetime); } /* if */ icp->bundled = TRUE; } /* if */ detach_ref_entries_from_curr_expr(operand); add_init_component_to_initializer_cache(icp, to_front, cache); if (expr_stack->consteval_function_designator_seen) { icp->consteval_function_designator_seen = TRUE; } /* if */ } /* add_operand_to_initializer_cache */ an_init_component_ptr fetch_init_component_from_initializer_cache( an_initializer_cache *cache) /* Remove the first initializer component from the indicated initializer cache and return it to the caller. If there is no cache (cache == NULL), or if the cache is empty, return NULL. */ { an_init_component_ptr icp = NULL; if (cache != NULL) { icp = cache->first_init; if (icp != NULL) { cache->first_init = next_elem(icp); if (cache->first_init == NULL) { cache->last_init = NULL; } /* if */ split_tail_elems(icp); } /* if */ } /* if */ return icp; } /* fetch_init_component_from_initializer_cache */ void extract_operand_from_expression_component(an_init_component_ptr icp, an_operand *operand, a_boolean free_icp) /* Extract an expression operand from the init-component given by icp, and return it in *operand. Free icp if free_icp is TRUE. Aside from copying the operand, this does whatever other processing might be needed to unbundle the operand. */ { an_operand *icp_operand; /* Issue an error if the thing removed is a braced-init-list rather than an expression. */ check_arg_list_elem_is_expression(icp); icp_operand = operand_of_arg_list_elem(icp); copy_operand(icp_operand, operand); unbundle_init_component_expressions(icp); if (free_icp) { /* Change the source operand so it no longer points to any of the entries in the subtree so they won't be freed (ownership is transferred to the returned operand). */ icp_operand->ref_entries_list = NULL; change_to_error_operand(icp_operand); free_init_component_list(icp); } /* if */ } /* extract_operand_from_expression_component */ a_boolean fetch_operand_from_initializer_cache(an_operand *operand, an_initializer_cache *cache) /* Remove the first expression from the indicated initializer cache, return it in *operand, and return TRUE. Free the init-component entry removed from the cache. If there is no cache (cache == NULL) or the cache is empty, return FALSE. If the entity removed from the cache is a braced-init-list, issue an error and return an error operand. If the expression is bundled, unbundle it; the current lifetime on the expression stack is discarded in that case. That implies that this routine must be called after the expression stack has been pushed. */ { a_boolean result = FALSE; an_init_component_ptr icp; icp = fetch_init_component_from_initializer_cache(cache); if (icp != NULL) { result = TRUE; extract_operand_from_expression_component(icp, operand, /*free_icp=*/TRUE); } /* if */ return result; } /* fetch_operand_from_initializer_cache */ static a_dynamic_init_dtor_fixup_ptr alloc_dynamic_init_dtor_fixup( a_dynamic_init_ptr dynamic_init, a_source_position *position) /* Allocate an entry to record a fixup to be done on the dynamic initialization entry dynamic_init. The associated source position is given by "position". The entry is put on the fixup list for the current expression context. */ { a_dynamic_init_dtor_fixup_ptr didfp; if (avail_dynamic_init_dtor_fixups != NULL) { /* Reuse a previously-freed entry. */ didfp = avail_dynamic_init_dtor_fixups; avail_dynamic_init_dtor_fixups = didfp->next; } else { /* Allocate a new entry. */ didfp = (a_dynamic_init_dtor_fixup_ptr) alloc_fe(sizeof(a_dynamic_init_dtor_fixup)); #if DEBUG num_dynamic_init_dtor_fixups_allocated++; #endif /* DEBUG */ } /* if */ didfp->next = expr_stack->dynamic_init_dtor_fixup_list; expr_stack->dynamic_init_dtor_fixup_list = didfp; didfp->dynamic_init = dynamic_init; didfp->position = *position; return didfp; } /* alloc_dynamic_init_dtor_fixup */ void free_dynamic_init_dtor_fixup(a_dynamic_init_dtor_fixup_ptr didfp) /* Free the dynamic init dtor fixup entry pointed to by didfp. */ { /* Add the entry to the available list. */ didfp->next = avail_dynamic_init_dtor_fixups; avail_dynamic_init_dtor_fixups = didfp; } /* free_dynamic_init_dtor_fixup */ void if_evaluating_mark_routine_referenced(a_routine_ptr routine) /* Mark the indicated routine as actually referenced, but only if the current expression is being evaluated. This routine is an interface to mark_routine_referenced. One effect of calling this function is that the given routine may be instantiated. */ { if (!expr_stack->template_deduction_context) { if (curr_expr_is_potentially_evaluated()) { /* A reference during deduction is "tentative" and therefore the routine should not be marked as referenced. */ /* Routines referenced in default argument expressions are not instantiated until there is a use of the default argument expression. */ mark_routine_referenced_full(routine, /*instantiate=*/ !expr_stack->is_default_arg_expression, /*elided_reference=*/ expr_stack->template_deduction_context); } else if (!is_template_dependent_context()) { a_symbol_ptr assoc_sym = symbol_for(routine); if (assoc_sym != NULL) { instantiate_exception_spec_if_needed(assoc_sym); } /* if */ } /* if */ } /* if */ } /* if_evaluating_mark_routine_referenced */ void expr_reference_to_implicitly_invoked_function_full( a_symbol_ptr sym, a_source_position *pos, a_type_ptr class_of_object, a_boolean honor_virtual, a_boolean evaluated, a_boolean instantiate) /* Interface to reference_to_implicit_invoked_function to be used when calling it from within the expression-processing routines. Supplies some parameters from values on the expression stack. */ { a_boolean error_detected = FALSE; a_boolean *p_error_detected = NULL; /* If errors are suppressed, get a returned variable instead of issuing any error. */ if (expr_stack->suppress_diagnostics) p_error_detected = &error_detected; reference_to_implicitly_invoked_function( sym, pos, class_of_object, honor_virtual, evaluated, instantiate, expr_access_checking_should_be_done(), /*elided_reference=*/FALSE, p_error_detected); if (error_detected) record_suppressed_error(); } /* expr_reference_to_implicitly_invoked_function_full */ void expr_reference_to_implicitly_invoked_function( a_symbol_ptr sym, a_source_position *pos, a_type_ptr class_of_object, a_boolean honor_virtual) /* Interface to reference_to_implicit_invoked_function to be used when calling it from within the expression-processing routines. Supplies more parameters from values on the expression stack. */ { /* Routines referenced in default argument expressions are not instantiated until there is a use of the default argument expression. */ expr_reference_to_implicitly_invoked_function_full( sym, pos, class_of_object, honor_virtual, curr_expr_is_potentially_evaluated(), /*instantiate=*/!expr_stack->is_default_arg_expression); } /* expr_reference_to_implicitly_invoked_function */ a_boolean expr_reference_to_trivial_default_constructor( a_type_ptr class_type, a_source_position *pos) /* Interface to reference_to_trivial_default_constructor to be used when calling it from within the expression-processing routines. Supplies some arguments from values on the expression stack. */ { a_boolean result; a_boolean error_detected = FALSE; a_boolean *p_error_detected = NULL; /* If errors are suppressed, get a returned variable instead of issuing any error. */ if (expr_stack->suppress_diagnostics) p_error_detected = &error_detected; result = reference_to_trivial_default_constructor( class_type, class_type, pos, expr_access_checking_should_be_done(), p_error_detected); if (error_detected) record_suppressed_error(); return result; } /* expr_reference_to_trivial_default_constructor */ void expr_reference_to_trivial_copy_constructor( a_type_ptr class_type, a_source_position *pos, a_boolean elided_reference) /* Interface to reference_to_trivial_copy_constructor to be used when calling it from within the expression-processing routines. Supplies some arguments from values on the expression stack. */ { a_boolean error_detected = FALSE; a_boolean *p_error_detected = NULL; /* If errors are suppressed, get a returned variable instead of issuing any error. */ if (expr_stack->suppress_diagnostics) p_error_detected = &error_detected; reference_to_trivial_copy_constructor(class_type, class_type, pos, expr_access_checking_should_be_done(), elided_reference, p_error_detected); if (error_detected) record_suppressed_error(); } /* expr_reference_to_trivial_copy_constructor */ an_expr_node_ptr expr_copy_default_arg_expr_list(a_routine_ptr rout, a_param_type_ptr ptp) /* Interface to copy_default_arg_expr_list to be used when calling it from within the expression-processing routines. Supplies the last three parameters from values on the expression stack. */ { an_expr_node_ptr expr; expr = copy_default_arg_expr_list( rout, ptp, (a_boolean)expr_stack->inside_conditional_expression, curr_expr_is_potentially_evaluated(), curr_expr_is_evaluated()); return expr; } /* expr_copy_default_arg_expr_list */ static void transfer_context_from_enclosing_expr_stack_entry( a_boolean direct, an_expr_stack_entry *old_entry, an_expr_stack_entry *new_entry) /* The entry new_entry is being pushed onto the expression stack, and old_entry is the enclosing stack entry. Transfer any context flags from the old to the new entry. For example, if the old entry is marked as inside a default argument expression, the new entry is so marked as well. If direct is TRUE, this is being done for a normal push of one entry an top of another. If it is FALSE, the connection is less direct; e.g., we went from expression processing to declaration processing and back to expression processing again, as in a decltype. */ { check_assertion(old_entry != NULL); /* is_template_arg_expression is not copied down, because it indicates the top level in a template argument expression. Likewise for is_vla_dimension_expression. in_cctor_elision_initializer is also not copied down; nested expressions in an elision initializer are not subject to the optimization. */ new_entry->is_default_arg_expression = old_entry->is_default_arg_expression; #if MICROSOFT_EXTENSIONS_ALLOWED new_entry->is_cli_attr_arg_expression = old_entry->is_cli_attr_arg_expression; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ new_entry->current_lambda_in_header = old_entry->current_lambda_in_header; new_entry->p_end_of_entities_defined_in_expression = old_entry->p_end_of_entities_defined_in_expression; #if GNU_EXTENSIONS_ALLOWED new_entry->marked_as_gnu_extension |= old_entry->marked_as_gnu_extension; #endif /* GNU_EXTENSIONS_ALLOWED */ new_entry->in_coroutine_desc_init |= old_entry->in_coroutine_desc_init; new_entry->paren_as_aggregate_init |= old_entry->paren_as_aggregate_init; new_entry->range_based_for_range |= old_entry->range_based_for_range; new_entry->in_constant_array_dimension |= old_entry->in_constant_array_dimension; new_entry->expansion_initializer |= old_entry->expansion_initializer; if (direct) { new_entry->evaluated = old_entry->evaluated; new_entry->potentially_evaluated = old_entry->potentially_evaluated; new_entry->is_type_operator_arg_expression = old_entry->is_type_operator_arg_expression; new_entry->inside_conditional_expression = old_entry->inside_conditional_expression; new_entry->template_deduction_context |= old_entry->template_deduction_context; new_entry->suppress_diagnostics |= old_entry->suppress_diagnostics; new_entry->possible_rescan_context |= old_entry->possible_rescan_context; new_entry->in_static_initializer |= old_entry->in_static_initializer; new_entry->in_call_argument |= old_entry->in_call_argument; } /* if */ } /* transfer_context_from_enclosing_expr_stack_entry */ void save_expr_stack(an_expr_stack_entry_ptr *saved_expr_stack) /* Clear the expression stack, returning the old expression stack pointer to the caller in *saved_expr_stack, for later restoration by calling restore_expr_stack. This is used at the start of processing of an expression that is not part of the surrounding context. */ { *saved_expr_stack = expr_stack; expr_stack = NULL; } /* save_expr_stack */ void restore_expr_stack(an_expr_stack_entry_ptr saved_expr_stack) /* Restore the expression stack to the state it had when save_expr_stack was called. */ { expr_stack = saved_expr_stack; } /* restore_expr_stack */ void push_expr_stack(an_expression_kind expression_kind, an_expr_stack_entry_ptr new_entry, a_boolean force_object_lifetime, a_boolean suppress_object_lifetime) /* Push a new entry on the top of the expr_stack. expression_kind indicates the kind of the expression. new_entry is used as the new top-of-stack entry (the entries are local variables on the stack). This is done at the start of a major expression. An object lifetime is pushed if necessary for a full expression; that's forced by force_object_lifetime TRUE (which is used to say that even if temporaries have lifetime to end-of-scope, this expression's temporaries need to be destroyed at the end of the expression, e.g., because it's an expression repeated in a loop). If suppress_object_lifetime is TRUE, no object lifetime is pushed regardless of any of the other factors. */ { /* A "full expression" is one not inside another expression. That's significant for the lifetime of temporaries. */ a_boolean full_expr = (expr_stack == NULL); new_entry->prev = expr_stack; new_entry->expression_kind = expression_kind; new_entry->old_ref_entries_list = curr_expr_ref_entries; curr_expr_ref_entries = NULL; new_entry->evaluated = TRUE; new_entry->potentially_evaluated = TRUE; new_entry->potentially_unevaluated = FALSE; new_entry->objectless_nonstatic_data_ref_seen = FALSE; new_entry->potentially_unevaluated_lambda_seen = FALSE; new_entry->is_type_operator_arg_expression = FALSE; new_entry->is_default_arg_expression = FALSE; new_entry->is_initial_default_arg_scan = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED new_entry->is_cli_attr_arg_expression = FALSE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ new_entry->is_template_arg_expression = FALSE; new_entry->is_vla_dimension_expression = FALSE; new_entry->in_cctor_elision_initializer = FALSE; new_entry->favor_constant_result = FALSE; new_entry->inside_conditional_expression = FALSE; new_entry->unevaluated_expr_will_be_kept_in_il = FALSE; new_entry->template_deduction_context = FALSE; new_entry->suppress_diagnostics = FALSE; new_entry->any_suppressed_error = FALSE; new_entry->possible_rescan_context = cpp11_sfinae_enabled && (expression_kind != (an_expression_kind)ek_pp) && is_template_deduction_context(); new_entry->in_static_initializer = FALSE; new_entry->next_stack_push_considered_same_expression = FALSE; new_entry->record_first_nonconstexpr_call = FALSE; new_entry->constant_expr_ruled_out = FALSE; new_entry->traditional_const_expr_required = FALSE; new_entry->in_noexcept_operand_expression = FALSE; new_entry->suppress_constexpr_call_folding = FALSE; if ((expression_kind == ek_integral_constant || expression_kind == ek_template_arg || (depth_scope_stack != NO_SCOPE_DEPTH && scope_stack_top().in_consteval_context)) && constexpr_enabled) { new_entry->consteval_call_need_not_fold = TRUE; } else { new_entry->consteval_call_need_not_fold = FALSE; } /* if */ new_entry->consteval_function_designator_seen = FALSE; new_entry->allow_call_with_incomplete_return_type = FALSE; new_entry->allow_array_decay_in_constant_expr = FALSE; new_entry->uses_this_operand = FALSE; new_entry->nodiscard_expr_seen = FALSE; #if GNU_EXTENSIONS_ALLOWED new_entry->marked_as_gnu_extension = FALSE; #endif /* GNU_EXTENSIONS_ALLOWED */ new_entry->fold_prvalue_if_possible = FALSE; new_entry->in_call_argument = FALSE; new_entry->in_coroutine_desc_init = FALSE; new_entry->paren_as_aggregate_init = FALSE; new_entry->expr_will_be_discarded = FALSE; new_entry->statement_expression_seen = FALSE; new_entry->likely_not_evaluated = FALSE; new_entry->trace_unevaluated_lambdas = FALSE; new_entry->range_based_for_range = FALSE; new_entry->in_constant_array_dimension = FALSE; new_entry->is_enumerator_value = FALSE; new_entry->expansion_initializer = FALSE; new_entry->const_eval_reattempt_state = {}; new_entry->dynamic_init_dtor_fixup_list = NULL; new_entry->nested_construct_depth = 0; new_entry->lifetime = NULL; new_entry->scope_number = (depth_scope_stack != NO_SCOPE_DEPTH) ? scope_stack[depth_scope_stack].number : NO_SCOPE_NUMBER; new_entry->destructions_preceding_expr = NULL; new_entry->last_subscope_preceding_expr = NULL; #if GENERATE_SOURCE_SEQUENCE_LISTS new_entry->last_source_seq_entry_preceding_expr = NULL; #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ new_entry->objectless_nonstatic_data_ref_pos = null_source_position; new_entry->potentially_unevaluated_lambda_pos = null_source_position; new_entry->call_in_right_comma_operand_pos = null_source_position; new_entry->current_lambda_in_header = NULL; new_entry->p_end_of_entities_defined_in_expression = NULL; new_entry->default_rescan_info = NULL; new_entry->initializer_cache = NULL; new_entry->rcblock = NULL; if (expr_stack != NULL) { /* There is a previous stack entry; set any of the flags that are affected by the enclosing stack entry. */ transfer_context_from_enclosing_expr_stack_entry(/*direct=*/TRUE, expr_stack, new_entry); } /* if */ expr_stack = new_entry; /*lint !e733 !e789*/ /* Do special handling for constant expressions. This is done late so that curr_expr_kind_is_const can be used. */ if (curr_expr_kind_is_const()) { /* Constant operations should be folded to constants inside constant expressions. */ /* Set the flag for pre-C++11 constant expression scanning. */ if (!constexpr_enabled || curr_expr_kind_is(ek_pp)) { new_entry->traditional_const_expr_required = TRUE; } /* if */ expr_stack->favor_constant_result = TRUE; /* Constant expressions are always evaluated even when inside a not-evaluated expression. For example, in sizeof(int[1+1]) the 1+1 must be evaluated. */ expr_stack->evaluated = TRUE; expr_stack->potentially_evaluated = TRUE; } else if (curr_expr_kind_is(ek_sizeof)) { /* An expression inside sizeof is not evaluated. This is also used for a number of other non-evaluated cases. */ expr_stack->evaluated = FALSE; expr_stack->potentially_evaluated = FALSE; expr_stack->potentially_unevaluated = TRUE; /* Save information needed to discard any side effects of the expression when it is discarded later. The caller can set unevaluated_expr_will_be_kept_in_il to TRUE after push_expr_stack to preserve the expression. */ { a_scope_stack_entry_ptr ssep = &scope_stack[depth_scope_stack]; expr_stack->last_subscope_preceding_expr = ssep->last_scope; #if GENERATE_SOURCE_SEQUENCE_LISTS expr_stack->last_source_seq_entry_preceding_expr = ssep->end_of_source_sequence_list; #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ } } /* if */ if (!C_mode() && !suppress_object_lifetime && full_expr) { /* Full expression in C++ mode. We may want to start an object lifetime. Do so if the lifetime of temporaries is a full expression, or if the caller explicitly requests a lifetime. Don't push a lifetime in a constant expression or in an unevaluated expression. */ if ((!long_lifetime_temps || force_object_lifetime) && expression_kind == ek_normal) { push_object_lifetime(iek_none, (char *)NULL, (an_object_lifetime_kind)olk_expr_temporary); expr_stack->lifetime = curr_object_lifetime; } else { /* Don't start a new lifetime, but remember the destructions pointer from the current lifetime so we can identify any added for the new expression. */ expr_stack->destructions_preceding_expr = curr_object_lifetime->destructions; } /* if */ } /* if */ } /* push_expr_stack */ void push_expr_stack_with_rcblock( an_expression_kind expression_kind, an_expr_stack_entry_ptr new_entry, a_boolean force_object_lifetime, a_boolean suppress_object_lifetime, a_rescan_control_block *rcblock) /* Interface to push_expr_stack for contexts where there may be a rescan control block (rcblock) and if there is one we'd like to record it in the expression stack. */ { push_expr_stack(expression_kind, new_entry, force_object_lifetime, suppress_object_lifetime); new_entry->rcblock = rcblock; } /* push_expr_stack_with_rcblock */ void push_expr_stack_for_initializer(an_expr_stack_entry *expr_stack_entry, an_expr_stack_entry **saved_expr_stack, an_expression_kind expr_kind, a_boolean is_full_expr, a_decl_parse_state *dps, an_init_state *is) /* Push an expression stack entry for the scanning or processing of an initializer. "dps" and/or "is" describe the entity being initialized. Both can be NULL, e.g., for a ctor-initializer. is_full_expr is TRUE if the initializer is to be treated as full-expression. expr_stack_entry is the new stack entry to be pushed, and saved_expr_stack is used to save the existing expression stack for later restoration in the case of a full expression. expr_kind is the kind of expression stack entry to be pushed; however, it will be forced to ek_init_constant if dps/is indicate the initializer must be constant and expr_kind is not already a constant expression kind. */ { if (is == NULL && dps != NULL) is = &dps->init_state; if (dps == NULL && is != NULL) dps = is->decl_parse_state; if (is_full_expr) { save_expr_stack(saved_expr_stack); } else { *saved_expr_stack = NULL; check_assertion(expr_stack != NULL); } /* if */ if (is != NULL && is->initializer_must_be_constant) { if ((int)expr_kind > (int)ek_init_constant) { expr_kind = (an_expression_kind)ek_init_constant; } /* if */ } else if (is != NULL && is->not_evaluated) { expr_kind = (an_expression_kind)ek_sizeof; } /* if */ push_expr_stack(expr_kind, expr_stack_entry, /*force_object_lifetime=*/FALSE, /*suppress_object_lifetime=*/FALSE); if (is_full_expr) { transfer_expr_context_if_applicable(*saved_expr_stack); if (is != NULL) is->elements_are_full_expressions = FALSE; } /* if */ if (is != NULL && is->static_lifetime_init) { expr_stack_entry->in_static_initializer = TRUE; expr_stack_entry->favor_constant_result = TRUE; } else if (favor_constant_result_for_nonstatic_init) { expr_stack_entry->favor_constant_result = TRUE; } /* if */ if (is != NULL) { /* Transfer some additional flags from the init state. */ expr_stack_entry->traditional_const_expr_required = is->traditional_const_expr_required; if (!is->not_potentially_evaluated) { expr_stack_entry->potentially_evaluated = TRUE; } else { /* If an initializer is not potentially evaluated, it is certainly not evaluated at all. */ check_assertion(is->not_evaluated); } /* if */ } /* if */ if (dps != NULL && dps->sym != NULL) { if (symbol_is(dps->sym, sk_static_data_member) || symbol_is(dps->sym, sk_field)) { /* Record entities defined in the initializer expression for a static data member (needed for correspondence checking and name mangling when the static data member is a template instance). In the case of aggregate initializers, this routine may be called multiple times for the same initializer: Ensure that additional entries are appended to any existing entries. */ an_il_entity_list_entry_ptr *ep; if (symbol_is(dps->sym, sk_static_data_member)) { a_variable_ptr sdm_var = dps->sym->variant.static_data_member.variable; ep = &sdm_var->entities_defined_in_initializer; } else { ep = &dps->sym->variant.field.ptr->entities_defined_in_initializer; } /* if */ while (*ep != NULL) ep = &(*ep)->next; expr_stack_entry->p_end_of_entities_defined_in_expression = ep; } /* if */ if (symbol_is(dps->sym, sk_field)) { /* For a field initializer, try to get a constant result if possible. */ expr_stack->favor_constant_result = TRUE; } /* if */ } /* if */ if (dps != NULL) set_up_initializer_rescan(dps); } /* push_expr_stack_for_initializer */ void transfer_expr_context_if_applicable(an_expr_stack_entry *saved_stack) /* The expression stack has been saved and cleared by save_expr_stack; *saved_stack is the saved top of the expression stack. Now, a new stack entry has been pushed. If it appears that the new expression is part of the same context as the previous stack entry, transfer context flags to the new entry. For example, if the old entry indicates we're inside of a default argument expression, mark the new entry the same way. */ { if (saved_stack != NULL && expr_stack != NULL) { if (saved_stack->next_stack_push_considered_same_expression) { transfer_context_from_enclosing_expr_stack_entry(/*direct=*/TRUE, saved_stack, expr_stack); } else if (expr_stack->rcblock != NULL) { if (expr_stack->rcblock == saved_stack->rcblock) { transfer_context_from_enclosing_expr_stack_entry(/*direct=*/TRUE, saved_stack, expr_stack); } /* if */ } else if (saved_stack->scope_number != NO_SCOPE_NUMBER && saved_stack->scope_number == expr_stack->scope_number) { transfer_context_from_enclosing_expr_stack_entry(/*direct=*/FALSE, saved_stack, expr_stack); } /* if */ /* Always preserve "inside a lambda header" across a save/restore of the expression stack (e.g., when scanning a noexcept-specifier or a trailing decltype). Nested lambdas push their own expression-stack entry and clear this field before calling record_start_of_lambda_header, so this does not interfere with nesting. */ if (expr_stack->current_lambda_in_header == NULL) { expr_stack->current_lambda_in_header = saved_stack->current_lambda_in_header; } /* if */ } /* if */ } /* transfer_expr_context_if_applicable */ void set_up_initializer_rescan(a_decl_parse_state *dps) /* If the initializer for an entity was previously scanned and saved in dps->prescanned_initializer_cache, activate that initializer cache so that the expression will be consumed next before any more expressions are scanned. Otherwise, do nothing. */ { check_assertion(dps != NULL); if (anything_cached(&dps->prescanned_initializer_cache)) { check_assertion(expr_stack != NULL && expr_stack->initializer_cache == NULL); expr_stack->initializer_cache = &dps->prescanned_initializer_cache; } /* if */ } /* set_up_initializer_rescan */ void undo_side_effects_for_discarded_unevaluated_expression(void) /* The expression associated with the current level of the expression stack is unevaluated and is being discarded. Undo any global side effects like additions to the source sequence list (such things can happen because of GNU statement expressions in the expression). */ { a_scope_stack_entry_ptr ssep = &scope_stack_top(); check_assertion(expr_stack != NULL && curr_expr_kind_is(ek_sizeof) && !expr_stack->potentially_evaluated); ssep->last_scope = expr_stack->last_subscope_preceding_expr; if (ssep->last_scope == NULL) { ssep->first_scope = NULL; } else { ssep->last_scope->next = NULL; } /* if */ #if GENERATE_SOURCE_SEQUENCE_LISTS { a_source_sequence_entry_ptr ss_ptr = expr_stack->last_source_seq_entry_preceding_expr; if (ss_ptr != NULL && ss_ptr->next != NULL) { /* Clear the entity so that pruning during scope pop drops the entries. */ clear_src_seq_list_segment(ss_ptr->next, (a_source_sequence_entry_ptr)NULL); } /* if */ } #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ expr_stack->unevaluated_expr_will_be_kept_in_il = FALSE; } /* undo_side_effects_for_discarded_unevaluated_expression */ void pop_expr_stack(void) /* Pop the top entry off the expr_stack. This is done at the end of a major expression. */ { an_expr_stack_entry_ptr new_top; if (expr_stack->lifetime != NULL) { /* An object lifetime was pushed for the expression, so it must be popped now. */ check_assertion(curr_object_lifetime == expr_stack->lifetime); if (curr_expr_kind_is_const()) { /* Discard any object lifetime created in a constant expression (errors were issued). */ discard_constant_expr_object_lifetime(); } /* if */ (void)pop_object_lifetime(); } /* if */ /* Flush the reference entries list for the current expression. */ flush_ref_entries_list(); /* Restore the old reference entries list, if any. */ curr_expr_ref_entries = expr_stack->old_ref_entries_list; if (curr_expr_kind_is(ek_sizeof) && !expr_stack->potentially_evaluated && !expr_stack->unevaluated_expr_will_be_kept_in_il) { undo_side_effects_for_discarded_unevaluated_expression(); } /* if */ if (expr_stack->rcblock != NULL && expr_stack->any_suppressed_error) { /* If an error was detected in a rescan, record it in the rescan control block. */ subst_fail(expr_stack->rcblock->error_detected); } /* if */ new_top = expr_stack->prev; if (new_top != NULL) { /* If entities were defined in the subexpression associated with the entry to be popped, make sure that is reflected in the entry for the enclosing expression. */ if (new_top->p_end_of_entities_defined_in_expression != NULL) { /* Append the list of entities recorded in the entry being popped to the list of entries in the new top-of-stack. */ while (*new_top->p_end_of_entities_defined_in_expression != NULL) { new_top->p_end_of_entities_defined_in_expression = &(*new_top->p_end_of_entities_defined_in_expression)->next; } /* while */ } /* if */ /* If we detected an error during template deduction, propagate that up if the parent stack entry is tracking such things. */ if (expr_stack->any_suppressed_error && new_top->template_deduction_context) { new_top->any_suppressed_error = TRUE; } /* if */ if (expr_stack->statement_expression_seen) { new_top->statement_expression_seen = TRUE; } /* if */ } /* if */ /* Pop the stack. */ expr_stack = new_top; } /* pop_expr_stack */ void pop_expr_stack_for_initializer(an_expr_stack_entry *saved_expr_stack, a_boolean is_full_expr, a_decl_parse_state *dps, an_init_state *is) /* Pop the expression stack at the end of scanning an initializer. The parameters match the corresponding ones for push_expr_stack_for_initializer. */ { if (is == NULL && dps != NULL) is = &dps->init_state; if (dps == NULL && is != NULL) dps = is->decl_parse_state; if (dps != NULL && dps->sym != NULL && (symbol_is(dps->sym, sk_static_data_member) || symbol_is(dps->sym, sk_field))) { /* Stop the recording of entities defined in the expression (not strictly necessary, but just to be neat). */ expr_stack->p_end_of_entities_defined_in_expression = NULL; } /* if */ if (is != NULL) { /* Copy the constant_expr_ruled_out flag from the expression stack into the init state. That flag may then be carried up to another expression stack in cases that involve a -> -> transition. */ is->constant_expr_ruled_out = expr_stack->constant_expr_ruled_out; } /* if */ if (dps != NULL && expr_stack->const_eval_reattempt_state.default_mem_init) { /* The expression stack has a reattempt state that indicates there's a constant expression evaluation that should be reattempted for any future uses as a default member initializer; this requires the initializer to be considered per-constructor, and thus the initializer needs to be marked as "ctor dependent." */ dps->init_state.is_ctor_dependent = TRUE; } /* if */ pop_expr_stack(); if (is_full_expr) { restore_expr_stack(saved_expr_stack); if (is != NULL) is->elements_are_full_expressions = TRUE; } /* if */ } /* pop_expr_stack_for_initializer */ void temporarily_set_non_constant_expression_kind( an_expression_kind *saved_kind, a_boolean *saved_traditional) /* Temporarily set a non-constant expression kind so that something that is normally not allowed in a constant expression will be accepted. Save some values in saved_kind and saved_traditional for restoration through a call to restore_constant_expression_kind. */ { check_assertion(expr_stack != NULL && curr_expr_kind_is_const()); *saved_kind = expr_stack->expression_kind; *saved_traditional = expr_stack->traditional_const_expr_required; expr_stack->expression_kind = (an_expression_kind)ek_normal; expr_stack->traditional_const_expr_required = FALSE; } /* temporarily_set_non_constant_expression_kind */ void restore_constant_expression_kind(an_expression_kind saved_kind, a_boolean saved_traditional) /* Undo the temporary change to the expression kind set up by temporarily_set_non_constant_expression_kind. */ { check_assertion(expr_stack != NULL); expr_stack->expression_kind = saved_kind; expr_stack->traditional_const_expr_required = saved_traditional; } /* restore_constant_expression_kind */ void record_entity_defined_in_expression(char *entity, an_il_entry_kind kind, a_boolean in_file_scope) /* If there is an active expression stack and it currently is set up to record entities defined in expressions, record the given entity in the list managed through the expression stack. If in_file_scope is TRUE, the list entry is allocated in file scope memory; otherwise, the current memory region is used. */ { if (expr_stack != NULL && expr_stack->p_end_of_entities_defined_in_expression != NULL) { a_memory_region_number region_to_switch_back_to; an_il_entity_list_entry_ptr ep; if (in_file_scope) switch_to_file_scope_region(®ion_to_switch_back_to); ep = alloc_il_entity_list_entry(); ep->entity.kind = kind; ep->entity.ptr = (char*)entity; *expr_stack->p_end_of_entities_defined_in_expression = ep; expr_stack->p_end_of_entities_defined_in_expression = &ep->next; if (in_file_scope) { switch_back_to_original_region(region_to_switch_back_to); } /* if */ } /* if */ } /* record_entity_defined_in_expression */ a_boolean entities_are_recorded_for_current_expression(void) /* Return TRUE if there is an active expression stack and it currently is set up to record entities defined in expressions. */ { return (expr_stack != NULL && expr_stack->p_end_of_entities_defined_in_expression != NULL); } /* entities_are_recorded_for_current_expression */ void rule_out_expr_kinds(a_ruled_out_expr_kind_set ruled_out_set, an_operand *operand) /* Set flags in the operand "operand" indicating that the kinds of expressions indicated by the bit set ruled_out_set are ruled out, meaning that constructs have appeared in the expression that are not allowed to appear in the ruled-out kinds of expressions. This is not an error; we're just keeping track so that we can ask later if the expression qualifies as, for example, an integral constant expression. This mechanism is not used in C++11 mode. */ { /* These flags are not maintained in C++11 mode. There, we only care whether an entire expression is constant, not about subexpressions. If an unevaluated operand of a "?" doesn't have the form of a constant in C++11 mode, for example, that's fine; the overall expression can still be constant. */ if (!constexpr_enabled) { if (ruled_out_set & ROEK_CONSTANT) { /* Record the fact that the entire current expression is nonconstant. */ expr_stack->constant_expr_ruled_out = TRUE; /* If a constant expression is ruled out, an integral constant expression is ruled out also. */ ruled_out_set |= ROEK_INTEGRAL_CONSTANT; } /* if */ operand->ruled_out_expr_kinds |= ruled_out_set; } /* if */ } /* rule_out_expr_kinds */ #if SEQUENCING_DIAGNOSTICS_ENABLED static void issue_sequencing_diagnostic(a_seq_pt_info_entry_ptr spiep) /* If a sequencing diagnostic hasn't been previously emitted, emit one now. Typically, the same diagnostic is issued multiple times in the same expression (once for each unsequenced side-effect or use). */ { if (!spiep->diagnostic_issued) { a_source_position *pos = &spiep->expr->expr_range.start; if (expr_diagnostic_should_be_issued( es_remark, ec_unsequenced_use_of_variable, pos)) { check_assertion(is_variable_node(spiep->expr)); pos_sy_diagnostic(es_remark, ec_unsequenced_use_of_variable, pos, symbol_for(node_variable(spiep->expr))); } /* if */ spiep->diagnostic_issued = TRUE; } /* if */ } /* issue_sequencing_diagnostic */ static a_seq_pt_info_entry_ptr check_for_unsequenced_uses( a_seq_pt_info_entry_ptr side_effects, a_seq_pt_info_entry_ptr uses) /* Issue diagnostics for every case where there's a side-effect and a use. Returns a pointer to the last "use" on the list of uses. */ { a_seq_pt_info_entry_ptr effect, use, result = NULL; for (use = uses; use != NULL; use = use->next) { for (effect = side_effects; effect != NULL; effect = effect->next) { issue_sequencing_diagnostic(effect); issue_sequencing_diagnostic(use); } /* for */ result = use; } /* for */ return result; } /* check_for_unsequenced_uses */ static void complete_outstanding_side_effects( a_seq_pt_var_entry_ptr spvep, a_boolean entire_list) /* A sequencing operation has occurred that forces completion of all side-effects; reset those side-effects that are independent of value computation. If entire_list is TRUE, process all entries on spvep; otherwise just operate on spvep itself. */ { a_seq_pt_info_entry_ptr se; for (; spvep != NULL; spvep = spvep->next) { for (se = spvep->side_effects; se != NULL; se = se->next) { if (se->independent_of_value_computation) { se->independent_of_value_computation = FALSE; } /* if */ } /* for */ if (!entire_list) break; } /* for */ } /* complete_outstanding_side_effects */ static void merge_seq_pt_info_lists(a_seq_pt_var_entry_ptr lspvep, a_seq_pt_var_entry_ptr rspvep, a_boolean sequenced, a_boolean is_assignment) /* This routine merges two sets of sequencing information pertaining to the same variable. Items are moved from rspvep to lspvep (so that rspvep is effectively empty on return). When sequenced is TRUE, all of the side-effects and uses in lspvep occur before any side-effect or use in rspvep, so no sequencing diagnostics are generated. When sequenced is FALSE, a sequencing diagnostic is issued if a side-effect in one list is found with either a side-effect or a use on the other list. When is_assignment is TRUE, the side-effect in rspvep represents the modification of object in an assignment expression. */ { a_seq_pt_info_entry_ptr se1, se2, use, last = NULL; check_assertion(lspvep->variable == rspvep->variable); if (lspvep->side_effects == NULL) { /* No side-effects in the first expression; the resulting list is whatever is in the second expression. */ lspvep->side_effects = rspvep->side_effects; } else if (rspvep->side_effects == NULL) { /* No side-effects in the second expression; reset the independent_of_value_computation flag on all side-effects in the first expression (e.g., a case like "(x++, x)" where the post-increment side-effect has been sequenced). */ if (sequenced) { complete_outstanding_side_effects(lspvep, /*entire_list=*/FALSE); } /* if */ } else { /* Both expressions have side-effects on the same variable. */ if (is_assignment) { /* In this case we're adding the side-effect for an assignment expression; the side-effect is sequenced after the value computation of both of the operands (lspvep reflects the merged lists of both right and left operands of the assignment expression). Only side-effects that are unaffected by value computation will conflict with this new side-effect (e.g., "x = ++x" is okay, but "x = x++" is unsequenced). */ check_assertion(rspvep->side_effects->next == NULL); for (se1 = lspvep->side_effects; se1 != NULL; se1 = se1->next) { if (se1->independent_of_value_computation) { issue_sequencing_diagnostic(se1); issue_sequencing_diagnostic(rspvep->side_effects); } /* if */ } /* for */ last = rspvep->side_effects; } else { if (sequenced) { complete_outstanding_side_effects(lspvep, /*entire_list=*/FALSE); } else { for (se1 = lspvep->side_effects; se1 != NULL; se1 = se1->next) { /* This un-sequenced side-effect must conflict with at least one other side-effect, so issue a diagnostic. */ issue_sequencing_diagnostic(se1); } /* for */ } /* if */ for (se2 = rspvep->side_effects; se2 != NULL; se2 = se2->next) { if (!sequenced) { issue_sequencing_diagnostic(se2); } /* if */ last = se2; } /* for */ } /* if */ } /* if */ if (last != NULL) { /* Merge the two lists of side-effects. */ last->next = lspvep->side_effects; lspvep->side_effects = rspvep->side_effects; } /* if */ if (sequenced) { /* The entries are sequenced, no need for diagnostics, just find the end of the second list. */ last = NULL; for (use = rspvep->uses; use != NULL; use = use->next) { last = use; } /* for */ } else { /* Issue diagnostics if uses and side-effects for the two lists conflict. */ (void)check_for_unsequenced_uses(rspvep->side_effects, lspvep->uses); last = check_for_unsequenced_uses(lspvep->side_effects, rspvep->uses); } /* if */ if (last != NULL) { /* Merge the two lists of uses. */ last->next = lspvep->uses; lspvep->uses = rspvep->uses; } /* if */ rspvep->uses = NULL; rspvep->side_effects = NULL; } /* merge_seq_pt_info_lists */ static a_seq_pt_var_entry_ptr merge_lists_and_check_for_sequencing_issues( a_seq_pt_var_entry_ptr lspvep, a_seq_pt_var_entry_ptr rspvep, a_boolean sequenced, a_boolean is_assignment) /* Return a list of sequence point variable entries that contains entries from both lspvep and rspvep, issuing diagnostics when appropriate. lspvep contains a list of sequence point variable entries for the left operand (or merged left-most operands) and rspvep contains a list of sequence point variable entries for the right (or next) operand. sequenced is TRUE when the expression imposes a left-to-right sequencing on the operands (i.e., comma, question, logical and, and logical or operations). is_assignment is TRUE when the side-effect in rspvep is the side-effect associated with an assignment expression. */ { a_seq_pt_var_entry_ptr copy, lprev = NULL; a_seq_pt_var_entry_ptr result; if (lspvep == NULL) { result = rspvep; } else if (rspvep == NULL) { result = lspvep; } else { /* Merge rspvep's elements into lspvep. */ result = lspvep; while (lspvep != NULL && rspvep != NULL) { /* The two lists are sorted using the variable's name as a key. */ int ret = 0; a_boolean found = FALSE; if (lspvep->variable == rspvep->variable) { /* This variable is common to both lists; merge the two lists of side-effects into one list (lspvep), possibly issuing diagnostics. */ merge_seq_pt_info_lists(lspvep, rspvep, sequenced, is_assignment); lprev = lspvep; lspvep = lspvep->next; copy = rspvep; rspvep = rspvep->next; /* Now that the lists have been merged, free the unused list. */ free_seq_pt_entry_for_var(copy); found = TRUE; } else if ((ret = strcmp(lspvep->variable->source_corresp.name, rspvep->variable->source_corresp.name)) == 0) { /* The entry on rspvep has the same name as an entry on lspvep, but doesn't match the first such variable; see if there are any other similarly named variables on the list that match. */ a_seq_pt_var_entry_ptr ptr = lspvep; for (ptr = lspvep; ptr != NULL; ptr = ptr->next) { if (ptr->variable == rspvep->variable) { /* This variable is common to both lists; merge the two lists of side-effects into one list (lspvep), possibly issuing diagnostics. */ merge_seq_pt_info_lists(ptr, rspvep, sequenced, is_assignment); copy = rspvep; rspvep = rspvep->next; /* Now that the lists have been merged, free the unused list. */ free_seq_pt_entry_for_var(copy); found = TRUE; /* Don't update lspvep (so it'll still point to the first variable with this name). */ break; } /* if */ if (strcmp(ptr->variable->source_corresp.name, rspvep->variable->source_corresp.name) != 0) break; } /* for */ } /* if */ if (!found) { if (ret >= 0) { /* Variable is on rspvep list but not lspvep; move it to lspvep. */ if (lprev == NULL) { result = rspvep; } else { lprev->next = rspvep; } /* if */ lprev = rspvep; copy = rspvep->next; rspvep->next = lspvep; rspvep = copy; } else { /* Variable is on lspvep list but not rspvep; leave it. */ lprev = lspvep; lspvep = lspvep->next; } /* if */ } /* if */ } /* while */ if (rspvep != NULL) { /* Everything on rspvep comes after everything on lspvep; move the items from rspvep to lspvep. */ check_assertion(lprev != NULL); lprev->next = rspvep; } /* if */ } /* if */ return result; } /* merge_lists_and_check_for_sequencing_issues */ #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ static a_boolean examine_expr_list_for_unordered_issues( an_expr_node_ptr expr_list, a_boolean sequenced, an_expr_or_stmt_traversal_block_ptr tblock) /* Examine the list of expressions headed by expr_list, and their subtrees, looking for unordered enk_temp_init initializations or unsequenced expressions with side-effects. If any unordered enk_temp_inits are found, mark their dynamic initialization entries as unordered. If tblock->set_unordered_on_dynamic_inits is TRUE, mark all enk_temp_init dynamic initializations as unordered (because of something detected higher up in the expression tree). If sequenced is TRUE, the expressions on the list are sequenced in some order (e.g., because there is a sequence point after the first expression on the list). Creates a list (in tblock->seq_pt_var_list) of side-effects and uses of variables in the expression (when sequencing diagnostics are enabled). Return TRUE if there are any temp inits (unordered or not) in the expression list. */ { a_boolean saved_result = tblock->result; a_boolean saved_set_unordered_on_dynamic_inits = tblock->set_unordered_on_dynamic_inits; a_boolean any_temp_inits = FALSE; an_expr_node_ptr expr, first_expr_with_temp_init = NULL; #if SEQUENCING_DIAGNOSTICS_ENABLED a_seq_pt_var_entry_ptr expr_seq_pt_var_list = NULL; a_seq_pt_var_entry_ptr save_seq_pt_var_list = tblock->seq_pt_var_list; #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ for (expr = expr_list; expr != NULL; expr = expr->next) { a_boolean curr_expr_has_temp_init; /* See whether this expression contains any temp inits. */ tblock->result = FALSE; #if SEQUENCING_DIAGNOSTICS_ENABLED tblock->seq_pt_var_list = NULL; #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ traverse_expr(expr, tblock); curr_expr_has_temp_init = tblock->result; any_temp_inits |= curr_expr_has_temp_init; if (curr_expr_has_temp_init && /* If we already know we're in an unordered region, we don't need to do any further checking or processing. We've already marked any temp inits in the current expression as unordered. */ !tblock->set_unordered_on_dynamic_inits) { /* There is at least one temp init in this expression. */ /* If there was a previous operand with a temp init, there's an ordering problem. */ if (first_expr_with_temp_init != NULL) { if (!sequenced) { /* There was a previous expression with a temp init, and the current expression has one as well, so there is an ordering issue. Go through the expressions up to this point and set the unordered flag in them. If there are more expressions following the current one, they will be processed as unordered as we continue through the list. */ an_expr_node_ptr texpr; a_boolean saved_relink_dynamic_inits = tblock->relink_dynamic_inits; tblock->set_unordered_on_dynamic_inits = TRUE; tblock->relink_dynamic_inits = FALSE; for (texpr = first_expr_with_temp_init;; texpr = texpr->next) { traverse_expr(texpr, tblock); if (texpr == expr) break; } /* for */ tblock->relink_dynamic_inits = saved_relink_dynamic_inits; } /* if */ } else { /* This is the first expression that has temp inits on the list. */ first_expr_with_temp_init = expr; } /* if */ } /* if */ #if SEQUENCING_DIAGNOSTICS_ENABLED if (sequencing_diagnostics_enabled) { /* Check for the presence of side-effects in unsequenced operations. Most operations are unsequenced, that is, their operands can be executed in any order. This can cause undefined behavior in cases where operands have side-effects (like increment, decrement, assignment). tblock->seq_pt_var_list has been set to a list of side-effects and uses in the current operand. Merge that list with the side-effects and uses of any previous operands in this expression. */ expr_seq_pt_var_list = merge_lists_and_check_for_sequencing_issues( expr_seq_pt_var_list, tblock->seq_pt_var_list, sequenced, /*is_assignment=*/FALSE); } /* if */ #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ } /* for */ #if SEQUENCING_DIAGNOSTICS_ENABLED if (save_seq_pt_var_list != NULL) { /* This can occur when dynamic inits are intermixed with expressions, e.g., "A *a = new (x++) A(x++);". In this case, merge the two lists (assuming they're not sequenced). */ expr_seq_pt_var_list = merge_lists_and_check_for_sequencing_issues( save_seq_pt_var_list, expr_seq_pt_var_list, /*sequenced=*/FALSE, /*is_assignment=*/FALSE); } /* if */ tblock->seq_pt_var_list = expr_seq_pt_var_list; #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ tblock->result = saved_result | any_temp_inits; tblock->set_unordered_on_dynamic_inits= saved_set_unordered_on_dynamic_inits; return any_temp_inits; } /* examine_expr_list_for_unordered_issues */ static void update_last_processed_dynamic_init( a_dynamic_init_ptr dip, an_expr_or_stmt_traversal_block_ptr tblock) /* The dynamic initialization entry pointed to by dip has been encountered while traversing an expression tree looking for unordered enk_temp_inits. Record it in tblock->last_relinked_dynamic_init as the latest entry encountered. If dip is not the expected next entry, rearrange the destructions list. This comes up when user-defined conversions are added to arguments of an overloaded function call. When tblock->last_relinked_dynamic_init is NULL, assume the entry is in the right place; other entries from the expression will be moved following it as necessary when they come up (this is necessary in long lifetime temporaries mode, where there might be unrelated destructions preceding the first destruction from the expression). */ { /* Process only entries on a lifetime list, and only those on the current lifetime list */ if (tblock->relink_dynamic_inits && dip->lifetime != NULL && dip->lifetime == curr_object_lifetime) { if (tblock->last_relinked_dynamic_init != NULL && dip->next_in_destruction_list != tblock->last_relinked_dynamic_init) { /* The destruction is not at the right place on the list. Move it. */ a_dynamic_init_ptr tdip = curr_object_lifetime->destructions; /* Remove dip from the destructions list. */ if (tdip == dip) { /* It's the first entry on the list. */ curr_object_lifetime->destructions = dip->next_in_destruction_list; } else { /* It's not the first entry on the list. Find the entry preceding it. */ for (;; tdip = tdip->next_in_destruction_list) { #if CHECKING if (tdip == NULL) { #if DEBUG fprintf(f_debug, "Dynamic init not found:\n"); db_dynamic_initializer(dip, 2); db_object_lifetime(curr_object_lifetime); #endif /* DEBUG */ unexpected_condition_str2("update_last_processed_dynamic_init:", "dip not found"); } /* if */ #endif /* CHECKING */ if (tdip->next_in_destruction_list == dip) break; } /* for */ tdip->next_in_destruction_list = dip->next_in_destruction_list; } /* if */ /* Insert dip in the list, preceding tblock->last_relinked_dynamic_init. */ tdip = curr_object_lifetime->destructions; if (tdip == tblock->last_relinked_dynamic_init) { /* Insert at the beginning of the list. */ curr_object_lifetime->destructions = dip; } else { /* Find the point at which the insert is to be done. */ for (;; tdip = tdip->next_in_destruction_list) { #if CHECKING if (tdip == NULL) { #if DEBUG fprintf(f_debug, "Dynamic init to insert:\n"); db_dynamic_initializer(dip, 2); fprintf(f_debug, "Dynamic init not found:\n"); db_dynamic_initializer(tblock->last_relinked_dynamic_init, 2); db_object_lifetime(curr_object_lifetime); #endif /* DEBUG */ unexpected_condition_str2("update_last_processed_dynamic_init:", "insert point not found"); } /* if */ #endif /* CHECKING */ if (tdip->next_in_destruction_list == tblock->last_relinked_dynamic_init) break; } /* for */ tdip->next_in_destruction_list = dip; } /* if */ dip->next_in_destruction_list = tblock->last_relinked_dynamic_init; } /* if */ tblock->last_relinked_dynamic_init = dip; } /* if */ } /* update_last_processed_dynamic_init */ static void examine_dynamic_init_for_unordered_issues( a_dynamic_init_ptr dip, an_expr_or_stmt_traversal_block_ptr tblock) /* Called from the expression traversal routines to process a dynamic initialization entry as part of looking for unordered temp inits, or unsequenced side-effects. */ { if (dip->kind == (a_dynamic_init_kind)dik_constructor) { /* Handle the constructor case here, because the expressions in the argument list might be unordered. */ a_boolean sequenced = dip->variant.constructor.has_sequenced_arguments; (void)examine_expr_list_for_unordered_issues( dip->variant.constructor.args, sequenced, tblock); tblock->suppress_subtree_walk = TRUE; } /* if */ if (dip->lifetime != NULL) { /* Tell the caller the tree contains a temp init. */ tblock->result = TRUE; if (tblock->set_unordered_on_dynamic_inits) { /* We're in a part of the tree that has unordered initializations, so mark this one as unordered. */ dip->unordered = TRUE; } /* if */ } /* if */ } /* examine_dynamic_init_for_unordered_issues */ static void examine_expr_for_unordered_issues( 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 looking for unordered temp inits or unsequenced side-effects. */ { a_boolean sequenced; an_expr_operator_kind op; #if SEQUENCING_DIAGNOSTICS_ENABLED a_seq_pt_var_entry_ptr spvep; a_seq_pt_info_entry_ptr spiep; #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ an_expr_node_ptr opnd_list; switch (expr->kind) { case enk_operation: op = expr->variant.operation.kind; sequenced = FALSE; switch (op) { case eok_land: case eok_lor: case eok_vector_land: case eok_vector_lor: case eok_comma: case eok_question: case eok_vector_question: /* Operators with a sequence point after the first operand never have unordered operands. The two-operand cases like a && b obviously have no ordering issues, and in a ? b : c b and c are not considered unordered with respect to one another because only one of the two expressions will be evaluated. */ sequenced = TRUE; break; default: /* The expression is sequenced if an explicit evaluation order has been specified. */ sequenced = expr->variant.operation.eval_left_to_right || expr->variant.operation.eval_right_to_left; if (sequenced && is_call_node(expr)) { /* Even with the C++17 evaluation order, there is no specified sequencing of arguments to a call, so assume that any call with multiple arguments is unsequenced. That could result in false positives (e.g., (i++, f)(0, i++) is properly sequenced and will result in a remark) but it catches the more likely f(i++, i++) case. */ an_expr_node_ptr arg = expr->variant.operation.operands->next; if (node_operator_is(expr, eok_points_to_member_call) || node_operator_is(expr, eok_dot_member_call) || node_operator_is(expr, eok_points_to_pm_call) || node_operator_is(expr, eok_dot_pm_call)) { arg = arg->next; } /* if */ if (arg != NULL && arg->next != NULL) { sequenced = FALSE; } /* if */ } /* if */ break; } /* switch */ /* The operands of an operation can be unordered or ordered right-to-left, so handle them specially. */ opnd_list = expr->variant.operation.operands; /* For operations that are evaluated right-to-left, temporarily reverse the order of the list so that destructors will be handled in the correct order. */ reverse_simple_list_if(expr->variant.operation.eval_right_to_left, opnd_list); (void)examine_expr_list_for_unordered_issues(opnd_list, sequenced, tblock); /* Restore the list order if necessary. */ reverse_simple_list_if(expr->variant.operation.eval_right_to_left, opnd_list); #if SEQUENCING_DIAGNOSTICS_ENABLED if (sequencing_diagnostics_enabled && !tblock->set_unordered_on_dynamic_inits) { a_boolean independent_of_value_computation = FALSE; a_boolean is_assignment = FALSE; an_expr_node_ptr var_node; switch (op) { case eok_call: /* All side-effects of a call are completed before the function is invoked. */ complete_outstanding_side_effects(tblock->seq_pt_var_list, /*entire_list=*/TRUE); break; case eok_post_decr: case eok_post_incr: /* Most side-effects don't last through a value computation, but post-increment and post-decrement are independent of value computation. */ independent_of_value_computation = TRUE; goto process_side_effect; 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_pre_decr: /* defined as += */ case eok_pre_incr: /* defined as -= */ is_assignment = TRUE; process_side_effect: var_node = expr->variant.operation.operands; if (is_variable_node(var_node) && has_name(node_variable(var_node))) { /* This expression modifies the value of a variable; allocate a side-effect entry that captures this information and see if it conflicts with any existing side-effects or uses for the variable. */ /* Note that we're ignoring side-effects on operands that aren't variables (e.g., fields). Further, only operations with side-effects that directly operate on variables are handled here, so for example, "(1, x) = x++" isn't flagged. An addressing path traversal of this node could be made, (though it would need to handle cases like "(0 ? y : x ) = x++" where there are multiple lvalues). */ check_assertion(var_node->is_lvalue); spvep = alloc_seq_pt_var_entry(node_variable(var_node)); spiep = alloc_sequence_info_entry(var_node); spiep->independent_of_value_computation = independent_of_value_computation; spiep->next = spvep->side_effects; spvep->side_effects = spiep; tblock->seq_pt_var_list = merge_lists_and_check_for_sequencing_issues( tblock->seq_pt_var_list, spvep, is_assignment, is_assignment); } /* if */ break; default: break; } /* switch */ } /* if */ #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ tblock->suppress_subtree_walk = TRUE; break; #if SEQUENCING_DIAGNOSTICS_ENABLED case enk_variable: if (sequencing_diagnostics_enabled && !tblock->set_unordered_on_dynamic_inits && !expr->is_lvalue && has_name(node_variable(expr))) { /* Record a use of the variable in this expression. */ spvep = seq_pt_entry_for_var(&tblock->seq_pt_var_list, node_variable(expr)); spiep = alloc_sequence_info_entry(expr); spiep->next = spvep->uses; spvep->uses = spiep; } /* if */ break; #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ case enk_new_delete: /* There are no ordering issues for new and delete, though perhaps that requires some explanation. For delete, there's just the one operand, so there's no ordering issue. For new, generally the sequence is (1) call of operator new to allocate, (2) set up freeing on exception, (3) do initialization. That's all done in order (you can't free something until you've allocated it, and you can't initialize it until you've allocated it and set up the freeing on exception during the initialization). In the case where the allocation is folded into a constructor call or a runtime call (e.g., array new), there will be no arguments for "new" and no freeing-on-destruction entry, so the initialization is the only thing left and therefore it has no ordering issues. */ break; case enk_statement: /* GNU statement expressions are much like function calls. */ tblock->suppress_subtree_walk = TRUE; break; default: /* There are no ordering issues for other cases. */ break; } /* switch */ } /* examine_expr_for_unordered_issues */ #if SEQUENCING_DIAGNOSTICS_ENABLED static void examine_constant_for_unordered_issues( a_constant_ptr con, an_expr_or_stmt_traversal_block_ptr tblock) /* Called from the expression traversal routines to process a constant as part of looking for unsequenced side-effects. */ { if (con->kind == (a_constant_repr_kind)ck_aggregate && !tblock->set_unordered_on_dynamic_inits) { /* Each initializer in an aggregate is sequenced. */ a_seq_pt_var_entry_ptr save_seq_pt_var_list = tblock->seq_pt_var_list; a_seq_pt_var_entry_ptr con_seq_pt_var_list = NULL; for (con = con->variant.aggregate.first_constant; con != NULL; con = con->next) { tblock->seq_pt_var_list = NULL; traverse_constant(con, tblock); con_seq_pt_var_list = merge_lists_and_check_for_sequencing_issues( con_seq_pt_var_list, tblock->seq_pt_var_list, /*sequenced=*/TRUE, /*is_assignment=*/FALSE); } /* for */ tblock->seq_pt_var_list = merge_lists_and_check_for_sequencing_issues( save_seq_pt_var_list, con_seq_pt_var_list, /*sequenced=*/FALSE, /*is_assignment=*/FALSE); tblock->suppress_subtree_walk = TRUE; } /* if */ } /* examine_constant_for_unordered_issues */ #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ static void set_up_unordered_issues_traversal_block( an_expr_or_stmt_traversal_block_ptr tblock) /* Set up the control block used for the unordered issues traversal. */ { clear_expr_or_stmt_traversal_block(tblock); tblock->process_expr = examine_expr_for_unordered_issues; tblock->process_dynamic_init = examine_dynamic_init_for_unordered_issues; /* We'll also be relinking dynamic initialization entries in the order in which we encounter them. The routine for that must be called after the subtree is walked to get the linking in the right order. */ tblock->process_post_dynamic_init = update_last_processed_dynamic_init; #if SEQUENCING_DIAGNOSTICS_ENABLED if (sequencing_diagnostics_enabled) { tblock->process_constant = examine_constant_for_unordered_issues; } /* if */ #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ tblock->relink_dynamic_inits = TRUE; } /* set_up_unordered_issues_traversal_block */ static a_boolean curr_expr_may_contain_unordered_temp_inits(void) /* Return TRUE if the current expression potentially contains unordered enk_temp_inits. This is tested by checking whether there are two or more temp inits in the whole expression. */ { a_boolean may_contain_unordered_temp_inits = TRUE; a_dynamic_init_ptr curr_destruction = curr_object_lifetime->destructions; a_dynamic_init_ptr after_last_destruction = expr_stack->destructions_preceding_expr; if (curr_destruction == after_last_destruction || curr_destruction->next_in_destruction_list == after_last_destruction) { /* The dynamic init contains no temp inits or only one, so there can be no unordered temp inits. */ may_contain_unordered_temp_inits = FALSE; } /* if */ return may_contain_unordered_temp_inits; } /* curr_expr_may_contain_unordered_temp_inits */ static a_boolean consteval_escalation() /* Return TRUE if the current context is a consteval function definition. This includes the case of "consteval escalation" as specified in P2564R3: The caller has checked that a potential escalation event (like the failure to fold a call to a consteval function) has occurred. */ { a_routine *curr_rp = curr_routine_or_null(); a_boolean result = FALSE; if (curr_rp != NULL && (curr_rp->is_consteval || (rout_is_real_template_instance(curr_rp) && curr_rp->is_declared_constexpr) || curr_rp->is_defaulted || curr_rp->is_lambda_body)) { /* This is either already a consteval function or a constexpr function with a synthesized definition (e.g., through template instantiation), and in the latter case "constexpr" implicitly reduces to "consteval". */ curr_rp->is_consteval = TRUE; result = TRUE; } /* if */ return result; } /* consteval_escalation */ static void diagnose_consteval_routine_node( an_expr_node_ptr node, an_expr_or_stmt_traversal_block_ptr tblock) /* If the given node is an enk_routine node or an enk_constant node for a pointer- to-member-function constant and the associated routine is consteval, issue an error indicating that the address of the consteval function cannot be used in that context. */ { if (is_operation_node(node)) { if (node->variant.operation.is_consteval_call) { if (expr_error_should_be_issued() && !is_consteval_diag_deferred()) { a_diag_list diag_list; a_routine_ptr rp = get_constexpr_callee(node, &diag_list); a_constant_ptr con = local_constant(); check_assertion(rp != NULL); if (interpret_constexpr_call(node, /*is_constant_evaluated=*/TRUE, con, &diag_list)) { /* We know this evaluation failed before. It should not succeed now. */ unexpected_condition(); } else if (!consteval_escalation()) { a_diagnostic_ptr dp; dp = pos_sy_start_error(ec_consteval_call_nonconstant, &node->position, symbol_for(rp)); add_more_info_list(dp, &diag_list); end_diagnostic(dp); } /* if */ release_local_constant(&con); } /* if */ tblock->terminate = TRUE; } /* if */ } else if (node->kind == enk_const_eval_deferred) { /* This is a deferred consteval operation, there's nothing to check at this point. The consteval semantics should be enforced when this operation is transformed. */ tblock->suppress_subtree_walk = TRUE; } else { a_routine_ptr rp = NULL; if (is_routine_node(node)) { rp = node_routine(node); } else if (is_constant_node(node)) { a_constant_ptr cp = node_constant(node); if (constant_is(cp, ck_ptr_to_member) && cp->variant.ptr_to_member.is_function_ptr) { rp = cp->variant.ptr_to_member.variant.routine; } /* if */ } /* if */ if (rp != NULL && rp->is_consteval && !consteval_escalation()) { if (!rp->is_deleted) { expr_pos_error(ec_address_of_consteval_function_leaked, &node->position); } /* if */ tblock->terminate = TRUE; } /* if */ } /* if */ } /* diagnose_consteval_routine_node */ void diag_invalid_consteval_func_in_expr(an_expr_node_ptr expr) /* Traverse the given expression tree to find invalid references to consteval functions (i.e., references that don't appear under a call to a consteval function, which would be folded by now). */ { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = diagnose_consteval_routine_node; traverse_expr(expr, &tblock); } /* diag_invalid_consteval_func_in_expr */ void diag_invalid_consteval_func_in_dyn_init(a_dynamic_init_ptr dip) /* Traverse the given dynamic initializer entry find invalid references to consteval functions (i.e., references that don't appear under a call to a consteval function, which would be folded by now). */ { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = diagnose_consteval_routine_node; traverse_dynamic_init(dip, &tblock); } /* diag_invalid_consteval_func_in_dyn_init */ void instantiate_template_var_if_applicable(a_variable *vp) /* If the given variable is a template specialization that hasn't been instantiated yet, instantiate it now. (This might update the variable's type if it has a deduced type.) */ { if (!vp->used && vp->template_info != NULL && !vp->is_nonreal && vp->template_info->template_arg_list != NULL && !vp->is_specialized) { a_template_instance *tip = template_instance_for_symbol(symbol_for(vp)); if (tip->master_instance != NULL && (tip->template_sym->defined || (vp->initializer_in_class && vp->init_kind == initk_none)) && !master_instance_of(tip)->already_instantiated) { instantiate_template_variable(tip, /*is_new=*/FALSE, /*is_use=*/TRUE); } /* if */ } /* if */ } /* instantiate_template_var_if_applicable */ an_expr_node_ptr wrap_up_full_expression(an_expr_node_ptr expr) /* Do any processing required at the end of a "full expression" that is expr. Do nothing if the expression is not a full expression (according to the expr_stack). */ { an_object_lifetime_ptr lifetime = expr_stack->lifetime; if (expr_stack->prev == NULL) { /* Full expression. */ if (expr_stack->consteval_function_designator_seen && !expr_stack->consteval_call_need_not_fold && !is_template_dependent_context()) { /* If a consteval function designator was recorded, make sure it hasn't "leaked". */ diag_invalid_consteval_func_in_expr(expr); } /* if */ /* If the expression contains more than one enk_temp_init, see if they are unordered with respect to one another. This must be done at the end because of temp inits that get optimized out. */ /* Also issue diagnostics for sequencing issues (e.g., i = v[i++]). */ if ((!C_mode() && curr_expr_may_contain_unordered_temp_inits()) || sequencing_diagnostics_enabled) { an_expr_or_stmt_traversal_block tblock; set_up_unordered_issues_traversal_block(&tblock); traverse_expr(expr, &tblock); #if SEQUENCING_DIAGNOSTICS_ENABLED free_seq_pt_var_entry_list(tblock.seq_pt_var_list); #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ } /* if */ /* If the current expression has an associated object lifetime with something in it, add an enk_object_lifetime node on the top of the expression tree. */ if (!C_mode() && lifetime != NULL) { /* Check to see if the object lifetime has anything in it. If not, there is no need to add the enk_object_lifetime node. */ if (!is_useless_object_lifetime(lifetime)) { /* An error node stays the same. */ if (is_error_node(expr)) { mark_object_lifetime_as_useless(lifetime); } else { expr = add_object_lifetime_to_expr(expr, lifetime); } /* if */ } /* if */ } /* if */ if (!C_mode() && curr_expr_is_potentially_evaluated() && !expr_stack->is_default_arg_expression) { an_expr_node *node = skip_parens(expr); if (is_variable_node(node)) { /* If this is a reference to a variable template specialization, make sure the specialization is instantiated. It is not clear that a discarded-value expression like: template T x = (T*)0; void g() { x; } should trigger the instantiation of the variable (an error in this example), but it appears to be common practice to do so. */ instantiate_template_var_if_applicable(node_variable(node)); } /* if */ } /* if */ } /* if */ return expr; } /* wrap_up_full_expression */ void wrap_up_dynamic_init_full_expression(a_dynamic_init_ptr dip) /* Do any processing required at the end of a "full expression" that is the constructor call implied by a parenthesized initializer. dip points to the dynamic initialization. It can be NULL if there has been a previous error. */ { an_object_lifetime_ptr lifetime = expr_stack->lifetime; if (dip != NULL && !C_mode()) { /* If the initialization contains more than one enk_temp_init, see if they are unordered with respect to one another. This must be done at the end because of temp inits that get optimized out. Also check for any sequencing issues if so enabled. */ if (sequencing_diagnostics_enabled || curr_expr_may_contain_unordered_temp_inits()) { an_expr_or_stmt_traversal_block tblock; set_up_unordered_issues_traversal_block(&tblock); traverse_dynamic_init(dip, &tblock); #if SEQUENCING_DIAGNOSTICS_ENABLED free_seq_pt_var_entry_list(tblock.seq_pt_var_list); #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ } /* if */ if (expr_stack->consteval_function_designator_seen && !expr_stack->consteval_call_need_not_fold && !scope_stack_top().in_field_initializer && !is_template_dependent_context()) { diag_invalid_consteval_func_in_dyn_init(dip); } /* if */ } /* if */ if (!C_mode() && lifetime != NULL) { if (dip != NULL) { bind_object_lifetime(lifetime, iek_dynamic_init, (char *)dip); } else { /* Error. */ mark_object_lifetime_as_useless(lifetime); } /* if */ } /* if */ } /* wrap_up_dynamic_init_full_expression */ void wrap_up_constant_full_expression(a_constant *constant) /* Called at the end of scanning of a constant full-expression to do any final checking on the constant. pos is the source position of the constant. The expression stack entry for the constant expression has not yet been popped. */ { if (expr_stack->lifetime != NULL && !is_useless_object_lifetime(expr_stack->lifetime)) { /* An object lifetime with a constant result -- surprising. */ check_assertion(is_at_least_one_error() || expr_stack->any_suppressed_error || constant->kind == (a_constant_repr_kind)ck_template_param); discard_curr_expr_object_lifetime(); } /* if */ } /* wrap_up_constant_full_expression */ void discard_curr_expr_object_lifetime(void) /* If the current expression stack entry has an associated object lifetime, mark it so it will be discarded later. This is done for errors and for default argument expressions that are discarded, e.g., in prototype instantiations. */ { an_object_lifetime_ptr lifetime = expr_stack->lifetime; if (lifetime != NULL) mark_object_lifetime_as_useless(lifetime); } /* discard_curr_expr_object_lifetime */ void discard_constant_expr_object_lifetime(void) /* The current expression has turned out to be constant. If the object lifetime being maintained for it has destructions in it, discard them. */ { an_object_lifetime_ptr lifetime = expr_stack->lifetime; if (lifetime != NULL && lifetime->destructions != NULL) { discard_curr_expr_object_lifetime(); } /* if */ } /* discard_constant_expr_object_lifetime */ void set_operand_kind(an_operand *operand, an_operand_kind kind) /* Set the kind of the indicated operand. Also set associated variant fields to default values. */ { operand->kind = kind; /* Set the variant fields based on the kind of operand. */ switch (kind) { case ok_error: /* No variant fields. */ break; case ok_expression: operand->variant.expression = NULL; operand->symbol = NULL; break; case ok_constant: clear_constant(&operand->variant.constant, (a_constant_repr_kind)ck_error); break; case ok_indefinite_function: case ok_sym_for_member: case ok_undefined_symbol: operand->symbol = NULL; break; #if MICROSOFT_EXTENSIONS_ALLOWED case ok_property_ref: operand->variant.property_ref.object = NULL; operand->variant.property_ref.subscripts = NULL; break; case ok_event_ref: operand->variant.event_ref.object = NULL; break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case ok_braced_init_list: operand->variant.braced_init_list = NULL; break; default: unexpected_condition_str("set_operand_kind: bad kind"); } /* switch */ } /* set_operand_kind */ void clear_operand(an_operand_kind kind, an_operand *operand) /* Clear an operand and set its kind to "kind". Set other fields to default values. */ { /* Set the non-variant fields to a safe state. */ operand->type = NULL; #if OPTIMIZE_VIRTUAL_FUNCTION_CALLS operand->orig_routine_type = NULL; #endif /* OPTIMIZE_VIRTUAL_FUNCTION_CALLS */ operand->state = (an_operand_state)os_none; operand->bound_function = FALSE; operand->selector_is_object_pointer = FALSE; operand->virtual_function = FALSE; operand->is_id_expression = FALSE; operand->is_address_of_id_expression = FALSE; operand->id_expression_was_parenthesized = FALSE; operand->is_qualified_name = FALSE; operand->is_microsoft_deferred_name = FALSE; operand->access_control_error_reported = FALSE; operand->is_operand_of_address_of = FALSE; operand->has_required_ptr_to_member_form = FALSE; operand->is_template_id = FALSE; operand->is_simple_string_literal = FALSE; operand->conditional_simple_string_literal = FALSE; operand->is_cfront_null_pointer_constant = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED operand->is_microsoft_noop = FALSE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ operand->is_name_followed_by_left_paren = FALSE; operand->is_dummy_lvalue = FALSE; operand->is_parenthesized = FALSE; operand->name_reference_set = FALSE; operand->caused_template_instantiation = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED operand->allow_addr_of_managed_member = FALSE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ operand->pending_capture = FALSE; operand->name_reference = null_name_reference; operand->ruled_out_expr_kinds = ROEK_NONE; operand->position = null_source_position; #if EXTRA_SOURCE_POSITIONS_IN_IL operand->end_position = null_source_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ operand->ref_entries_list = NULL; operand->saved_ref_entries_list = NULL; operand->template_arg_list = NULL; operand->id_position = null_source_position; operand->ampersand_position = null_source_position; operand->pack_expansion_descr = NULL; operand->symbol = NULL; set_operand_kind(operand, kind); } /* clear_operand */ static an_expr_node_ptr maybe_get_operand_expr_with_pos(an_operand *operand) /* If the given operand is not an expression or is a compiler-generated expression, return NULL; otherwise, return the underlying expression node containing the position information to be queried or updated. */ { an_expr_node_ptr expr = expr_node_from_operand(operand); if (expr != NULL) { if (is_operation_node(expr) && node_operator_is(expr, eok_ref_indirect)) { /* Set the positions on the operand of the ref_indirect. */ expr = expr->variant.operation.operands; } /* if */ /* Don't set the position on a compiler-generated operation unless it is an operator-notation call node. */ if (is_operation_node(expr) && expr->compiler_generated && !expr->variant.operation.call_uses_operator_syntax) { expr = NULL; } /* if */ } /* if */ return expr; } /* maybe_get_operand_expr_with_pos */ void set_operand_expr_position_if_expr(an_operand *operand, a_source_position *operator_pos) /* If operand is an expression operand (or has an associated expression, such as a backing expression for a constant operand), set the source positions in the underlying expression node from the positions already in the operand. If operator_position is non-NULL, use that position as the operator position if setting the positions in the underlying expression node. */ { an_expr_node_ptr expr = maybe_get_operand_expr_with_pos(operand); if (expr != NULL) { /* Set the position in the expression node. */ if (operator_pos == NULL && expr->position.seq != 0) { /* If no operator position is provided, but one was already recorded in the node, preserve the recorded position. */ operator_pos = &expr->position; } /* if */ set_expr_position(expr, &operand->position, &operand->end_position, operator_pos); } /* if */ } /* set_operand_expr_position_if_expr */ static void set_operand_position_to_pos_curr_token(an_operand *operand) /* Set the position in the given operand to the current token position. If the operand is an expression, do not set the position in the expression. */ { operand->position = pos_curr_token; #if EXTRA_SOURCE_POSITIONS_IN_IL operand->end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* set_operand_position_to_pos_curr_token */ #if DEBUG void db_operand(an_operand *operand) /* Display an expression operand for debugging purposes. */ { switch (operand->state) { case os_none: break; case os_glvalue: if (is_an_xvalue(operand)) { (void)fprintf(f_debug, "xvalue, "); } else { (void)fprintf(f_debug, "lvalue, "); } /* if */ break; case os_prvalue: if (C_mode()) { (void)fprintf(f_debug, "rvalue, "); } else { (void)fprintf(f_debug, "prvalue, "); } /* if */ break; case os_function_designator: (void)fprintf(f_debug, "function, "); break; default: (void)fprintf(f_debug, ", "); break; } /* switch */ (void)fprintf(f_debug, "type = "); if (operand->type == NULL) { (void)fprintf(f_debug, "NULL"); } else { db_abbreviated_type(operand->type); } /* if */ (void)fprintf(f_debug, ", "); switch (operand->kind) { case ok_error: (void)fprintf(f_debug, "error"); break; case ok_expression: (void)fprintf(f_debug, "expression = \n"); db_expression(operand->variant.expression); break; case ok_constant: (void)fprintf(f_debug, "constant = "); db_constant(&operand->variant.constant); break; case ok_indefinite_function: (void)fprintf(f_debug, "indefinite function = "); db_symbol(operand->symbol, "", 0); break; case ok_sym_for_member: (void)fprintf(f_debug, "sym for member = "); db_symbol(operand->symbol, "", 0); break; case ok_undefined_symbol: (void)fprintf(f_debug, "undefined symbol = "); db_symbol(operand->symbol, "", 0); break; #if MICROSOFT_EXTENSIONS_ALLOWED case ok_property_ref: (void)fprintf(f_debug, "property ref = \n"); (void)fprintf(f_debug, "object ="); if (operand->variant.property_ref.object != NULL) { (void)fprintf(f_debug, "\n"); db_expression(operand->variant.property_ref.object); } else { (void)fprintf(f_debug, " NULL\n"); } /* if */ db_symbol(operand->symbol, "", 0); if (operand->variant.property_ref.subscripts != NULL) { an_arg_list_elem_ptr alep; (void)fprintf(f_debug, "\nsubscripts =\n"); for (alep = operand->variant.property_ref.subscripts; alep != NULL; alep = alep->next) { db_init_component(alep); } /* for */ } /* if */ break; case ok_event_ref: (void)fprintf(f_debug, "event ref = \n"); (void)fprintf(f_debug, "object ="); if (operand->variant.event_ref.object != NULL) { (void)fprintf(f_debug, "\n"); db_expression(operand->variant.event_ref.object); } else { (void)fprintf(f_debug, " NULL\n"); } /* if */ db_symbol(operand->symbol, "", 0); break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case ok_braced_init_list: db_init_component(operand->variant.braced_init_list); break; default: (void)fprintf(f_debug, ""); break; } /* switch */ (void)fprintf(f_debug, "\n"); } /* db_operand */ void db_ref_entries(a_ref_entry_ptr rep) /* Output a summary of the indicated list. */ { for (; rep != NULL; rep = rep->next_operand_ref) { db_symbol_name(rep->symbol); fprintf(f_debug, ":"); db_symbol_ref_kind(rep->kind); if (rep->already_recorded) { fprintf(f_debug, " RECORDED"); } /* if */ #if CHECKING if (rep->freed) { fprintf(f_debug, " FREED"); } /* if */ #endif /* CHECKING */ fprintf(f_debug, "\n"); } /* for */ } /* db_ref_entries */ void db_operand_ref_entries(an_operand *operand) /* Display a summary of the reference chain associated with an operand in the debug output. */ { db_ref_entries(operand->ref_entries_list); } /* db_operand_ref_entries */ #endif /* DEBUG */ an_expr_node_ptr alloc_empty_parens_func_cast( a_type_ptr type_cast_to, a_dynamic_init_kind init_kind, a_source_position *start_position) /* Allocate an expression that implements a functional-notation cast that has empty parentheses, e.g., T() or int(). init_kind indicates the kind of initialization (dik_zero usually, dik_none for some odd legacy cases). start_position gives the source start position. */ { an_expr_node_ptr temp_init_node; a_dynamic_init_ptr dip; if (is_error_type(type_cast_to)) { temp_init_node = error_node(); } else { temp_init_node = create_expr_temporary(type_cast_to, /*is_lvalue=*/FALSE, /*is_explicit_cast=*/TRUE, /* Abstract class test done previously. */ /*suppress_abstract_test=*/TRUE, init_kind, start_position, &dip); } /* if */ return temp_init_node; } /* alloc_empty_parens_func_cast */ a_dynamic_init_ptr add_array_nonconstant_aggregate_init( a_dynamic_init_ptr element_dip, a_type_ptr array_type, a_type_ptr elem_type, a_routine_ptr dtor_routine, a_targ_size_t number_of_elements) /* Change the indicated dynamic initialization into a dynamic initialization for each member of an array of classes. array_type is the type of the array, and elem_type is the type of the array elements. dtor_routine, if non-NULL, is the destructor associated with the element type. number_of_elements is the number of elements in the array, or 0 if the number of elements is variable (and known only at runtime). Multi-dimensional arrays are treated as single-dimensional arrays. Return a pointer to the dynamic init entry for the entire array. */ { a_dynamic_init_ptr array_dip; /* If exceptions are enabled, put in a destructor. It's needed to destroy elements if a throw is done part-way through the initialization (or destruction, for a delete) of the array. */ if (exceptions_enabled && dtor_routine != NULL) { record_dtor_in_dynamic_init(dtor_routine, element_dip, curr_expr_is_potentially_evaluated()); record_partial_aggregate_cleanup_destruction(element_dip, curr_expr_is_evaluated()); } /* if */ /* The IL structure is new dynamic init (dik_nonconstant_aggregate) -> constant (ck_aggregate) -> constant (ck_init_repeat) -> constant (ck_dynamic_init) -> original dynamic init (dik_constructor) */ array_dip = alloc_expr_dynamic_init((a_dynamic_init_kind)dik_nonconstant_aggregate); repeat_nonconstant_init(element_dip, array_type, elem_type, array_dip, number_of_elements); return array_dip; } /* add_array_nonconstant_aggregate_init */ void accumulate_array_size(a_type_ptr array_type, a_targ_size_t *num_elements) /* As part of determining the size of an array, multiply num_elements by the number of array elements in the first level of the array type given by array_type. For a variable or unknown-bound array, set *num_elements to zero. */ { a_type_ptr unqual_array_type = skip_typerefs(array_type); check_assertion(unqual_array_type->kind == (a_type_kind)tk_array); if (unqual_array_type->variant.array.is_template_dependent_size_array) { /* Arrays whose bounds are given by template-dependent constant expressions (in prototype instantiations) have unknown size. */ *num_elements = 0; } else if (vla_enabled && is_vla_type(unqual_array_type)) { /* VLA type. */ *num_elements = 0; } else { check_assertion(!has_unknown_specified_bound(unqual_array_type)); *num_elements *= unqual_array_type->variant.array.variant.number_of_elements; } /* if */ } /* accumulate_array_size */ a_dynamic_init_ptr add_array_nonconstant_aggregate_init_computing_size( a_dynamic_init_ptr element_dip, a_type_ptr array_type, a_routine_ptr dtor_routine) /* Interface to add_array_nonconstant_aggregate_init that determines the array element type and number of elements from the array type. */ { a_type_ptr elem_type = array_type; a_targ_size_t num_of_elements = 1; a_dynamic_init_ptr dip; /* Determine the number of elements in the (possibly multi-dimensional) array, or 0 if the number of elements is unknown at compile time, as for a template. */ while (is_array_type(elem_type)) { accumulate_array_size(elem_type, &num_of_elements); elem_type = array_element_type(elem_type); } /* while */ dip = add_array_nonconstant_aggregate_init(element_dip, array_type, elem_type, dtor_routine, num_of_elements); return dip; } /* add_array_nonconstant_aggregate_init_computing_size */ /* Macro that returns TRUE if the expression is a reused dynamic init. */ #define is_reused_dynamic_init(expr) \ ((expr)->kind == (an_expr_node_kind)enk_temp_init && \ ((expr)->variant.init.dynamic_init->is_reused_value)) an_expr_node_ptr make_expr_reusable_copy( an_expr_node_ptr expr, a_boolean vars_can_change, a_boolean *temp_init_used, a_boolean treat_as_potential_prvalue) /* Return a copy of the expression tree pointed to by expr, which is an prvalue or glvalue. If it is a glvalue, it is one whose address can be taken (e.g., the caller has eliminated bit-field lvalues). If the expression has side effects, or if its value is affected by the values of variables and vars_can_change is TRUE, the original expression will be changed so that its value (if it's a prvalue) or its address (if it's a glvalue) is stored in a temporary, and the copy will reference the temporary. vars_can_change TRUE means arbitrary user code might be executed in the interval between the original use of the expression and the use of the copy, and such code might change the values of (user) variables. *temp_init_used is returned TRUE if a temporary was used, including a reuse of an existing temporary. When it is TRUE, the caller must take steps to ensure that expr is evaluated before the copy. 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 value category is known at that time). */ { an_expr_node_ptr expr_copy, temp_init_expr; a_dynamic_init_ptr dip; /* Only callable from within the expression routines. See make_reusable_copy_full for a similar routine usable in IL lowering. */ check_assertion(expr_stack != NULL); *temp_init_used = FALSE; if (is_reused_dynamic_init(expr)) { /* The expression is already a reused value, probably generated by a previous call of this routine. Use the value again. */ dip = expr->variant.init.dynamic_init; expr_copy = alloc_expr_node((an_expr_node_kind)enk_reuse_value); expr_copy->variant.reused_value_init = dip; expr_copy->type = expr->type; *temp_init_used = TRUE; } else if (is_operation_node(expr) && node_operator_is(expr, eok_indirect) && is_reused_dynamic_init(expr->variant.operation.operands)) { /* The expression is an indirection on top of a reused value, probably generated by a previous call of this routine with an lvalue expression. Use the value again (with an indirection -- just like the source). */ dip = expr->variant.operation.operands->variant.init.dynamic_init; expr_copy = alloc_expr_node((an_expr_node_kind)enk_reuse_value); expr_copy->variant.reused_value_init = dip; expr_copy->type = make_pointer_type(expr->type); expr_copy = add_indirection_to_node(expr_copy); *temp_init_used = TRUE; } else if (is_invariant_expr(expr, vars_can_change, treat_as_potential_prvalue)) { /* The expression has no side effects and will give the same value if evaluated more than once. */ /* A straight copy will work. */ /* Note that this expression will not have temporaries or object lifetimes in it since it has no side effects. */ an_expr_copy_options_set copy_options = expr_stack->inside_conditional_expression ? CE_INSIDE_CONDITIONAL_EXPRESSION : CE_NO_OPTIONS; expr_copy = copy_expr_tree(expr, copy_options); } else { /* Change the original expression to a dynamic initialization marked with is_reused_value, and use an enk_reuse_value for the reuse. For an lvalue case, the temporary is set to the address of the lvalue, so the original expression "lvalue" becomes something like "*(temp = &lvalue)". */ a_type_ptr temp_type = expr->type; an_expr_node_ptr expr_to_save = copy_node(expr); a_boolean lvalue_case = expr->is_lvalue; if (lvalue_case) { expr_to_save = add_address_of_to_node(expr_to_save); temp_type = expr_to_save->type; } else { /* The operand shouldn't be a class in C++ (we don't want to copy it). */ check_assertion(C_mode() || !is_class_struct_union_type(temp_type)); } /* if */ dip = alloc_expr_dynamic_init((a_dynamic_init_kind)dik_expression); dip->variant.expression = expr_to_save; dip->is_reused_value = TRUE; temp_init_expr = alloc_temp_init_node(temp_type, dip, /*is_lvalue=*/FALSE, /*is_explicit_cast=*/FALSE); if (lvalue_case) { temp_init_expr = add_indirection_to_node(temp_init_expr); } /* if */ overwrite_node(expr, temp_init_expr); /* Make the expression for the reuse of the temporary. For an lvalue, put a "*" on top of that because the temporary is a pointer to the lvalue. */ expr_copy = alloc_expr_node((an_expr_node_kind)enk_reuse_value); expr_copy->variant.reused_value_init = dip; expr_copy->type = temp_type; if (lvalue_case) { expr_copy = add_indirection_to_node(expr_copy); } /* if */ *temp_init_used = TRUE; } /* if */ return expr_copy; } /* make_expr_reusable_copy */ static a_boolean is_bit_field_extract_node(an_expr_node_ptr node) /* Return TRUE if the given expression node is a bit-field extraction. */ { a_boolean is_bit_field_extract = FALSE; if (is_operation_node(node) && (node_operator_is((node), eok_dot_field) || node_operator_is((node), eok_points_to_field)) && node_field((node)->variant.operation.operands->next)->is_bit_field) { is_bit_field_extract = TRUE; } else if (is_variable_node(node)) { a_variable_ptr vp = node_variable(node); if (vp->init_kind == (an_init_kind)initk_binding) { is_bit_field_extract = is_bit_field_extract_node(vp->initializer.bound_expr); } /* if */ } /* if */ return is_bit_field_extract; } /* is_bit_field_extract_node */ an_expr_node_ptr glvalue_expr_reusable_copy( an_expr_node_ptr expr, a_boolean vars_can_change, a_reusable_copy_function_ptr copy_func, a_boolean *temp_init_used, a_boolean treat_as_potential_prvalue) /* Driver that handles glvalue special cases for making reusable copies of glvalues in both the front end proper and in IL lowering. Return a copy of the expression tree pointed to by expr, which is a glvalue. If the expression has side effects, or if its meaning is affected by the values of variables and vars_can_change is TRUE, the original expression will be changed so that some part of it is stored in a temporary, and the copy will reference the temporary. *temp_init_used is returned TRUE if a temporary was used, including a reuse of an existing temporary. When it is TRUE, the caller must take steps to ensure that expr is evaluated before the copy. copy_func points to a function that does a copy for prvalues and for simple glvalues (ones whose address can be taken); it's actually what defines the code for storing to a temporary and reusing it, and the decision on which cases should use a temporary. This function rewrites glvalues whose address cannot be taken, for example bit-field references, into simpler forms before calling the lower-level copy routine. 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 value category is known at that time). */ { an_expr_node_ptr expr_copy = NULL; *temp_init_used = FALSE; expr = skip_parens(expr); check_assertion(is_glvalue_node(expr) || is_error_node(expr)); if (is_bit_field_expr(expr)) { /* You can't take the address of a bit field, so break it down to underlying glvalues whose addresses can be taken. */ an_expr_operator_kind op; an_expr_node_ptr operand1, operand2, operand3; an_expr_node_ptr operand1_copy, operand2_copy, operand3_copy; a_boolean local_temp_init_used; check_assertion(is_operation_node(expr)); op = expr->variant.operation.kind; operand1 = expr->variant.operation.operands; operand2 = operand1->next; if (is_bit_field_extract_node(expr)) { /* For a bit-field selection, make a reusable copy of the struct address, then add the bit field selection to that. */ if (op == (an_expr_operator_kind)eok_dot_field) { operand1_copy = glvalue_expr_reusable_copy(operand1, vars_can_change, copy_func, temp_init_used, treat_as_potential_prvalue); } else { check_assertion(op == (an_expr_operator_kind)eok_points_to_field); operand1_copy = copy_func(operand1, vars_can_change, temp_init_used, treat_as_potential_prvalue); } /* if */ expr_copy = field_lvalue_selection_expr(operand1_copy, node_field(operand2)); copy_node_value_category(expr, expr_copy); } else if (op == (an_expr_operator_kind)eok_question || op == (an_expr_operator_kind)eok_vector_question) { /* For a "?" operator, make reusable copies of all three operands, and a new "?" that uses the reusable copies. */ operand3 = operand2->next; vars_can_change |= node_has_side_effects(expr, (a_boolean *)NULL); operand1_copy = copy_func(operand1, vars_can_change, temp_init_used, treat_as_potential_prvalue); #if DO_IL_LOWERING if (il_lowering_underway && op == (an_expr_operator_kind)eok_question) { /* We are producing a lowered eok_question and modifying an existing one: Ensure that both have a normalized first operand (in the configurations that require it). */ normalize_boolean_controlling_expr_if_needed(operand1); normalize_boolean_controlling_expr_if_needed(operand1_copy); } /* if */ #endif /* DO_IL_LOWERING */ operand2_copy = glvalue_expr_reusable_copy(operand2, vars_can_change, copy_func, &local_temp_init_used, treat_as_potential_prvalue); if (local_temp_init_used) *temp_init_used = TRUE; operand3_copy = glvalue_expr_reusable_copy(operand3, vars_can_change, copy_func, &local_temp_init_used, treat_as_potential_prvalue); if (local_temp_init_used) *temp_init_used = TRUE; operand1_copy->next = operand2_copy; operand2_copy->next = operand3_copy; expr_copy = make_lvalue_operator_node(op, expr->type, operand1_copy); expr_copy->variant.operation.returns_lvalue_instead_of_usual_rvalue = TRUE; copy_node_value_category(expr, expr_copy); } else if (op == (an_expr_operator_kind)eok_comma) { /* For a "," operator, make a reusable copy of the second operand. */ operand2 = operand1->next; vars_can_change |= node_has_side_effects(expr, (a_boolean *)NULL); expr_copy = glvalue_expr_reusable_copy(operand2, vars_can_change, copy_func, temp_init_used, treat_as_potential_prvalue); /* Note that the result is not a comma expression. */ #if GNU_EXTENSIONS_ALLOWED } else if (gpp_mode && is_gnu_min_max_operator(op)) { /* For the GNU C++ minimum and maximum operators, make reusable copies of both operands. */ vars_can_change |= node_has_side_effects(expr, (a_boolean *)NULL); operand1_copy = glvalue_expr_reusable_copy(operand1, vars_can_change, copy_func, &local_temp_init_used, treat_as_potential_prvalue); if (local_temp_init_used) *temp_init_used = TRUE; operand2_copy = glvalue_expr_reusable_copy(operand2, vars_can_change, copy_func, &local_temp_init_used, treat_as_potential_prvalue); if (local_temp_init_used) *temp_init_used = TRUE; operand1_copy->next = operand2_copy; expr_copy = make_lvalue_operator_node(op, expr->type, operand1_copy); check_assertion(!expr->is_xvalue); #endif /* GNU_EXTENSIONS_ALLOWED */ } else { unexpected_condition_str( "glvalue_expr_reusable_copy: unexpected bit field expr"); } /* if */ } else { /* For non-bit-field cases, use the simple copy. */ expr_copy = copy_func(expr, vars_can_change, temp_init_used, treat_as_potential_prvalue); } /* if */ return expr_copy; } /* glvalue_expr_reusable_copy */ static an_expr_node_ptr make_glvalue_expr_reusable_copy( an_expr_node_ptr expr, a_boolean vars_can_change, a_boolean *temp_init_used, a_boolean treat_as_potential_prvalue) /* Return a copy of the expression tree pointed to by expr, which is a glvalue. If the expression has side effects, or if its value is affected by the values of variables and vars_can_change is TRUE, the original expression will be changed so that some part of it is stored in a temporary, and the copy will reference the temporary. *temp_init_used is returned TRUE if a temporary was used, including a reuse of an existing temporary. When it is TRUE, the caller must take steps to ensure that expr is evaluated before the copy. 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). See make_lvalue_reusable_copy_full for a similar routine used in IL lowering. */ { an_expr_node_ptr expr_copy; expr_copy = glvalue_expr_reusable_copy(expr, vars_can_change, make_expr_reusable_copy, temp_init_used, treat_as_potential_prvalue); return expr_copy; } /* make_glvalue_expr_reusable_copy */ void clone_operand(an_operand *operand, an_operand *operand_clone, a_boolean vars_can_change, a_boolean *temp_init_used, a_boolean treat_as_potential_prvalue) /* Make a clone of the operand "operand" and put it in "operand_clone". The cloning process copies the subtrees of the operand, e.g., if it's an expression, a separate copy of the expression is made for the operand clone. The clone reuses the evaluation of the original expression rather than evaluating it again if it has side effects; *temp_init_used is returned TRUE if a temporary was used (including a reuse of an existing temporary), and the caller must ensure that operand is evaluated before the copy in that case. vars_can_change is TRUE if values of variables might change due to code executed between the first evaluation and the clone evaluation. The safe value is TRUE. treat_as_potential_prvalue is TRUE in cases where the eventual value category of "operand" 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 value category is known at that time). */ { *temp_init_used = FALSE; copy_operand(operand, operand_clone); /* Avoid having two operands pointing at the same reference list. */ operand_clone->ref_entries_list = NULL; switch (operand->kind) { case ok_error: case ok_indefinite_function: case ok_sym_for_member: case ok_undefined_symbol: /* No subtree to copy. */ break; case ok_constant: /* Avoid having two constants pointing at the same expression. */ operand->variant.constant.expr = NULL; break; case ok_expression: { an_expr_node_ptr expr; a_boolean class_prvalue_case = FALSE; if (!C_mode() && is_a_prvalue(operand) && is_class_struct_union_type(operand->type)) { /* In class cases, we don't want to make a copy of the class. We reuse the address of the class object we have. */ class_prvalue_case = TRUE; conv_class_operand_to_object_pointer(operand); check_assertion(is_expression_operand(operand)); } /* if */ expr = operand->variant.expression; if (is_a_glvalue(operand)) { expr = make_glvalue_expr_reusable_copy(expr, vars_can_change, temp_init_used, treat_as_potential_prvalue); } else { expr = make_expr_reusable_copy(expr, vars_can_change, temp_init_used, treat_as_potential_prvalue); } /* if */ operand_clone->variant.expression = expr; if (class_prvalue_case) { /* Restore the class prvalue for the original and the copy by adding an indirection plus a conversion to prvalue on both expressions. */ expr = add_indirection_to_node(operand->variant.expression); expr = conv_glvalue_expr_to_prvalue(expr, (a_boolean *)NULL, (a_constant_ptr *)NULL, &operand->position); operand->variant.expression = expr; operand->type = expr->type; expr = add_indirection_to_node(operand_clone->variant.expression); expr = conv_glvalue_expr_to_prvalue(expr, (a_boolean *)NULL, (a_constant_ptr *)NULL, &operand->position); operand_clone->variant.expression = expr; } /* if */ } break; #if MICROSOFT_EXTENSIONS_ALLOWED case ok_property_ref: if (operand->variant.property_ref.object != NULL) { operand_clone->variant.property_ref.object = make_expr_reusable_copy(operand->variant.property_ref.object, vars_can_change, temp_init_used, treat_as_potential_prvalue); } /* if */ /* Copy the list of subscript operands. */ { an_arg_list_elem_ptr alep, last_clone_alep = NULL; for (alep = operand->variant.property_ref.subscripts; alep != NULL; alep = next_elem(alep)) { a_boolean local_temp_init_used; an_arg_list_elem_ptr alep_clone = alloc_init_component((an_init_component_kind)ick_expression); /* The value category of subscript operators is not known at this point, so make sure treat_as_potential_prvalue is set to reflect this. */ check_assertion(is_expression_component(alep)); clone_operand(operand_of_arg_list_elem(alep), operand_of_arg_list_elem(alep_clone), vars_can_change, &local_temp_init_used, /*treat_as_potential_prvalue=*/TRUE); if (local_temp_init_used) *temp_init_used = TRUE; mark_init_component_as_permanently_allocated(alep_clone); if (last_clone_alep == NULL) { operand_clone->variant.property_ref.subscripts = alep_clone; } else { append_elem(last_clone_alep, alep_clone); } /* if */ last_clone_alep = alep_clone; } /* for */ } break; case ok_event_ref: if (operand->variant.event_ref.object != NULL) { operand_clone->variant.event_ref.object = make_expr_reusable_copy(operand->variant.event_ref.object, vars_can_change, temp_init_used, treat_as_potential_prvalue); } /* if */ break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case ok_braced_init_list: default: unexpected_condition_str("clone_operand: unexpected operand kind"); } /* switch */ } /* clone_operand */ an_expr_node_ptr strip_ref_indirect(an_expr_node_ptr expr, a_boolean parens_also) /* Strip an eok_ref_indirect node, if any, from the top of the given expression. If parens_also is TRUE, skip parentheses also. */ { if (parens_also) expr = skip_parens(expr); if (is_operation_node(expr) && node_operator_is(expr, eok_ref_indirect)) { expr = expr->variant.operation.operands; if (parens_also) expr = skip_parens(expr); } /* if */ return expr; } /* strip_ref_indirect */ an_expr_node_ptr find_primary_cast_node(an_expr_node_ptr orig_operand_expr, a_cast_source_form source_form, an_operand *operand) /* operand is the result of adding a cast (of source form described by source_form) to the original expression orig_operand_expr. Find and return the primary expression node for the cast, or return NULL if no such node exists. orig_operand_expr is used to test for the case where nothing is added to the original expression, e.g., for a do-nothing cast in some modes. orig_operand_expr can be NULL. */ { an_expr_node_ptr expr = expr_node_from_operand(operand); an_expr_node_ptr node_to_return = NULL; /* This routine is similar to strip_implicit_operations_for_rescan. */ while (expr != NULL && expr != orig_operand_expr) { /* Strip implicit operations above the cast. */ if (is_operation_node(expr)) { switch (expr->variant.operation.kind) { case eok_ref_indirect: case eok_array_to_pointer: case eok_lvalue: case eok_lvalue_adjust: case eok_class_rvalue_adjust: case eok_unbox_lvalue: /* These operations are always implicit. Keep stripping. */ break; default: if (expr->compiler_generated && (is_cast_operation_node(expr) || expr->variant.operation.is_conversion_call)) { /* Keep stripping for an implicit cast node. */ break; } /* if */ /* Something else. Stop stripping. */ goto end_of_loop; } /* switch */ expr = expr->variant.operation.operands; } else if (is_constant_node(expr)) { a_constant_ptr con = node_constant(expr); an_expr_node_ptr next_expr = NULL; if (con->kind == (a_constant_repr_kind)ck_template_param && !con->variant.template_param.do_not_rescan) { if (con->variant.template_param.kind == (a_template_param_constant_kind)tpck_expression) { next_expr = expr_node_from_tpck_expression(con); } /* if */ } /* if */ if (next_expr != NULL) { expr = next_expr; } else { goto end_of_loop; } /* if */ } else if (expr->kind == (an_expr_node_kind)enk_temp_init) { a_dynamic_init_ptr dip = expr->variant.init.dynamic_init; /* Explicit casts are retained. */ if (!is_generated_dynamic_init(dip)) goto end_of_loop; /* Anything else is implicit and stripped. */ expr = arg_list_from_dyn_init(dip); } else { break; } /* if */ } /* while */ end_of_loop: if (expr != NULL && expr != orig_operand_expr) { /* Return only a cast node, a temp-init node, or, for casts that allow user-defined-conversions, a conversion function call. */ if (expr->kind == (an_expr_node_kind)enk_temp_init || is_cast_operation_node(expr) || ((source_form == csf_static_cast || #if MICROSOFT_EXTENSIONS_ALLOWED source_form == csf_safe_cast || #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ source_form == csf_functional || source_form == csf_old_style) && is_operation_node(expr) && expr->variant.operation.is_conversion_call)) { check_assertion(!(is_operation_node(expr) && expr->compiler_generated)); node_to_return = expr; } /* if */ } /* if */ return node_to_return; } /* find_primary_cast_node */ static void clear_expr_rescan_info_entry(an_expr_rescan_info_entry_ptr eriep) /* Clear the fields of the given expression rescan info entry to default values. */ { clear_operand((an_operand_kind)ok_error, &eriep->saved_operand); eriep->expression_kind = (an_expression_kind)ek_normal; eriep->operator_position = null_source_position; eriep->operator_token_sequence_number = NO_TOKEN_SEQUENCE_NUMBER; eriep->secondary_operator_position = null_source_position; eriep->type = NULL; } /* clear_expr_rescan_info_entry */ static an_expr_rescan_info_entry_ptr alloc_expr_rescan_info_entry(void) /* Allocate an expression rescan information entry (used to save information needed to do a rescan of an expression to redo semantic analysis later), set its fields to default values, and return a pointer to it. */ { an_expr_rescan_info_entry_ptr eriep; eriep = (an_expr_rescan_info_entry_ptr)alloc_fe( sizeof(an_expr_rescan_info_entry)); #if DEBUG num_expr_rescan_info_entries_allocated++; #endif /* DEBUG */ clear_expr_rescan_info_entry(eriep); return eriep; } /* alloc_expr_rescan_info_entry */ an_expr_rescan_info_entry_ptr save_operand_info_in_rescan_info_entry( an_operand *operand, an_expr_rescan_info_entry_ptr eriep) /* The operand "operand" will potentially be rescanned later to redo semantic analysis for template deduction. Save any extra information from the operand in an expression rescan info entry. If eriep is non-NULL, it points to a pre-allocated entry. Otherwise, a new entry is allocated. In either case, a pointer to the filled-in rescan info entry is returned. */ { a_pack_expansion_descr *pedep = NULL; if (eriep == NULL) { eriep = alloc_expr_rescan_info_entry(); } else { pedep = eriep->saved_operand.pack_expansion_descr; } /* if */ /* Note that we do not clear all fields. In particular, operator_position and operator_token_sequence_number will often have been set previously by record_operator_position_in_expr_rescan_info. Similarly, we restore the pack-expansion information if it was previously recorded. */ eriep->saved_operand = *operand; eriep->expression_kind = expr_stack->expression_kind; if (pedep != NULL) { eriep->saved_operand.pack_expansion_descr = pedep; } /* if */ return eriep; } /* save_operand_info_in_rescan_info_entry */ static a_boolean is_explicitly_typed_operator_node(an_expr_node_ptr node) /* Return TRUE if the given node is one with a specific specified type (e.g., representing a cast or a new-expression). */ { if (is_constant_node(node)) { a_constant_ptr con = node_constant(node); if (constant_is(con, ck_template_param) && tpck_is(con, tpck_expression)) { node = expr_node_from_tpck_expression(con); } /* if */ } /* if */ return is_operation_node(node) ? is_cast_operation_node(node) : (node->kind == (an_expr_node_kind)enk_temp_init || #if MICROSOFT_EXTENSIONS_ALLOWED node->kind == (an_expr_node_kind)enk_gcnew || #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ node->kind == (an_expr_node_kind)enk_new_delete); } /* is_explicitly_typed_operator_node */ static void save_operand_info_in_expr_rescan_info_entry( an_operand *operand, an_expr_node_ptr node) /* The expression "node" represents the operand "operand", and it will potentially be rescanned later to redo semantic analysis for template deduction. Save any extra information from the operand in an expression rescan info entry attached to the expression node so it will be available for the rescan. */ { an_expr_rescan_info_entry_ptr eriep; eriep = save_operand_info_in_rescan_info_entry(operand, node->extra.rescan_info); node->extra.rescan_info = eriep; if (is_explicitly_typed_operator_node(node) && eriep->type == NULL) { /* A node with an explicitly-specified type (a cast or a new/gcnew): Record that type in the rescan info. */ if (node->kind == (an_expr_node_kind)enk_new_delete) { eriep->type = node->variant.new_delete->type; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (node->kind == (an_expr_node_kind)enk_gcnew) { eriep->type = node->variant.gcnew_info->type; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else if (node->orig_lvalue_type != NULL) { if (node->variant.operation.is_reference_cast) { if (node->variant.operation.is_rvalue_reference_cast) { eriep->type = make_rvalue_reference_type(node->orig_lvalue_type); #if MICROSOFT_EXTENSIONS_ALLOWED } else if (node->variant.operation.is_tracking_reference_cast) { eriep->type = make_tracking_reference_type(node->orig_lvalue_type); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else { eriep->type = make_reference_type(node->orig_lvalue_type); } /* if */ } else { eriep->type = node->orig_lvalue_type; } /* if */ } else { eriep->type = node->type; } /* if */ } /* if */ } /* save_operand_info_in_expr_rescan_info_entry */ void save_rescan_info_for_braced_init_list(a_dynamic_init_ptr dip, an_init_component_ptr icp) /* Record rescan information on the dynamic init entry given by dip, to save information about the braced-init-list that gave rise to that dynamic init, which is given by icp. */ { an_operand operand; an_expr_rescan_info_entry_ptr eriep; /* Note that the saved list is going to be used only for rescan purposes, never directly. So given that rescan mines its input to effectively reconstruct source, a lot of the information in the saved list will never be used. For example, saved ref entries in the operands in the list would never be reactivated and then completed. Also, if some of the operands are modified (e.g., to add implicit conversions) during the prototype instantiation, that doesn't matter; the rescan process knows to skip stuff like that. To possibly belabor the point: the operands in the saved list are "used" only once, in the prototype instantiation; they may thereafter be the basis of a rescan multiple times, but they will never be "used" again. */ make_braced_init_list_operand(icp, &operand); check_assertion(dip->rescan_info == NULL); eriep = save_operand_info_in_rescan_info_entry( &operand, (an_expr_rescan_info_entry *)NULL); dip->rescan_info = eriep; } /* save_rescan_info_for_braced_init_list */ void record_operator_position_in_expr_rescan_info( an_expr_node_ptr node, a_source_position *operator_position, a_token_sequence_number operator_tok_seq_number, a_source_position *operator_position_2) /* Record the operator position information in the rescan information attached to "node". Allocate the rescan info if necessary (that's the usual case). operator_position and operator_tok_seq_number give the operator's source position and token sequence number. If operator_position_2 is non-NULL, it gives a secondary operator position to be recorded. Do nothing if we're not currently in a possible rescan context. */ { if (expr_stack->possible_rescan_context) { an_expr_rescan_info_entry_ptr eriep = node->extra.rescan_info; if (eriep == NULL) { node->extra.rescan_info = eriep = alloc_expr_rescan_info_entry(); } /* if */ eriep->operator_position = *operator_position; eriep->operator_token_sequence_number = operator_tok_seq_number; if (operator_position_2 != NULL) { eriep->secondary_operator_position = *operator_position_2; } /* if */ } /* if */ } /* record_operator_position_in_expr_rescan_info */ void record_operator_position_in_rescan_info( an_operand *operand, a_source_position *operator_position, a_token_sequence_number operator_tok_seq_number, a_source_position *operator_position_2) /* Record the operator position and operator token sequence number in the rescan information associated with the expression attached to operand. Allocate the rescan info if necessary (the usual case is that the rescan information will need to be allocated). operator_position_2 can be used for a secondary operator position or type position. If no secondary position is needed, it should be NULL. If the operand does not have an associated expression (e.g., if it is a constant without a backing expression), do nothing. Also do nothing if we're not currently in a possible rescan context. */ { if (expr_stack->possible_rescan_context) { an_expr_node_ptr expr = expr_node_from_operand(operand); if (expr != NULL) { expr = strip_ref_indirect(expr, /*parens_also=*/FALSE); record_operator_position_in_expr_rescan_info(expr, operator_position, operator_tok_seq_number, operator_position_2); } /* if */ } /* if */ } /* record_operator_position_in_rescan_info */ void record_typed_operator_position_in_expr_rescan_info( an_expr_node_ptr expr, a_source_position *start_position, a_source_position *type_position, a_type_ptr type) /* Record the source position in the rescan information associated with an operation that involves a salient specified type (e.g., a cast), allocating the rescan info if needed. expr is the operation expression; start_position is the source position of the start of the operation; type_position is the source position of the type within the operation; and type is the specified type. */ { if (expr_stack->possible_rescan_context && !is_error_node(expr)) { an_expr_rescan_info_entry_ptr eriep; check_assertion(is_explicitly_typed_operator_node(expr)); record_operator_position_in_expr_rescan_info(expr, start_position, NO_TOKEN_SEQUENCE_NUMBER, type_position); eriep = expr->extra.rescan_info; check_assertion(eriep != NULL); eriep->type = type; } /* if */ } /* record_typed_operator_position_in_expr_rescan_info */ void record_cast_position_in_expr_rescan_info( an_expr_node_ptr expr, a_source_position *start_position, a_source_position *type_position, a_type_ptr cast_type) /* Record the source position of a cast in the rescan information associated with that cast, allocating the rescan info if needed. expr is the cast expression; start_position is the source position of the start of the cast; type_position is the source position of the type within the cast; and cast_type is the type cast to. */ { if (expr_stack->possible_rescan_context && !is_error_node(expr)) { if (is_operation_node(expr) && expr->variant.operation.is_conversion_call) { /* This is a non-dependent cast rendered as a call. Record the extra position in the form expected for a call. (Approximately: we don't have the opening and closing paren positions.) The construct will be rescanned later as a call, not a cast. */ record_operator_position_in_expr_rescan_info(expr, start_position, NO_TOKEN_SEQUENCE_NUMBER, start_position); } else { /* Normal case: a cast rendered as a cast. */ record_typed_operator_position_in_expr_rescan_info(expr, start_position, type_position, cast_type); if (expr->kind == (an_expr_node_kind)enk_temp_init) { /* Record the same rescan info in an associated dynamic init entry. This is useful in a case like a throw, where the dynamic init might later be separated from the enk_temp_init and used directly. */ a_dynamic_init_ptr dip = expr->variant.init.dynamic_init; if (dip->rescan_info == NULL) { dip->rescan_info = expr->extra.rescan_info; } else if (dip->rescan_info != expr->extra.rescan_info) { /* If the dynamic init already has rescan info, as it does when it records a braced-init-list, keep the saved_operand but copy the rest of the fields, so the type and positions are recorded. */ memcpy((char*)dip->rescan_info+sizeof(an_operand), (char*)expr->extra.rescan_info+sizeof(an_operand), sizeof(an_expr_rescan_info_entry)-sizeof(an_operand)); } /* if */ } /* if */ } /* if */ } /* if */ } /* record_cast_position_in_expr_rescan_info */ void record_cast_position_in_rescan_info(an_operand *operand, an_expr_node_ptr orig_operand_expr, a_cast_source_form source_form, a_source_position *start_position, a_source_position *type_position, a_type_ptr cast_type) /* Record the source position of a cast in the rescan information associated with that cast, allocating the rescan info if needed. operand is the cast expression; source_form indicates its source form; start_position is the source position of the start of the cast; type_position is the source position of the type within the cast; and cast_type is the type cast to. Do nothing if the operand is not discernibly a cast expression. orig_operand_expr is the expression for the operand before the cast was applied, used to detect cases where nothing was added for the cast. For casts that will always add something, or when the original operand was not an expression (e.g., it was a constant), it can be NULL. */ { if (expr_stack->possible_rescan_context) { an_expr_node_ptr expr = find_primary_cast_node(orig_operand_expr, source_form, operand); if (expr != NULL) { record_cast_position_in_expr_rescan_info(expr, start_position, type_position, cast_type); } /* if */ } /* if */ } /* record_cast_position_in_rescan_info */ void restore_operand_info_from_expr_rescan_info_entry( an_operand *operand, an_expr_rescan_info_entry_ptr eriep) /* Inverse of save_operand_info_in_expr_rescan_info_entry: restore in *operand any extra rescan information saved previously in *eriep. */ { /* This routine must be callable even when there is nothing on the expression stack. */ /* Don't restore bound_function; it is managed explicitly. */ a_boolean saved_bound_function = operand->bound_function; /* Ditto for pack_expansion_descr, */ a_pack_expansion_descr *saved_pack_expansion_descr = operand->pack_expansion_descr; restore_operand_details(operand, &eriep->saved_operand); operand->bound_function = saved_bound_function; operand->pack_expansion_descr = saved_pack_expansion_descr; } /* restore_operand_info_from_expr_rescan_info_entry */ void clear_rescan_control_block(a_rescan_control_block *rcblock) /* Clear a rescan control block to default values. */ { rcblock->expr = NULL; rcblock->operator_token = tok_error; rcblock->argument_list = NULL; rcblock->template_arg_list = NULL; rcblock->template_param_list = NULL; rcblock->options = CTWS_NO_OPTIONS; rcblock->ctws_state = NULL; rcblock->error_detected = FALSE; } /* clear_rescan_control_block */ an_expr_node_ptr strip_implicit_operations_for_rescan( an_expr_node_ptr expr, an_expr_rescan_info_entry_ptr *periep) /* Strip compiler-generated operations (e.g., implicit casts) from the top of the given expression and return the underlying expression. This is used as part of rescanning expressions in template deduction to redo semantic analysis. This function strips things that don't have a source counterpart and therefore shouldn't be rescanned. If periep is non-NULL, then *periep is set to point to the rescan information on the lowest node that has it. */ { if (periep != NULL) { if (expr->extra.rescan_info != NULL) { *periep = expr->extra.rescan_info; } else { *periep = NULL; } /* if */ } /* if */ for (;;) { /* This routine is similar to find_primary_cast_node. */ if (is_operation_node(expr)) { if ((expr->compiler_generated && is_cast_operation_node(expr)) || expr->variant.operation.implicit_step_of_explicit_cast) { /* Implicit cast -- keep stripping. */ expr = expr->variant.operation.operands; } else if (expr->variant.operation.is_conversion_call && expr->compiler_generated) { /* A call that implements an implicit conversion. Go to the second operand (the input to the conversion function). */ expr = expr->variant.operation.operands; check_assertion(expr->next != NULL && expr->next->next == NULL); expr = expr->next; } else { switch (expr->variant.operation.kind) { case eok_reference_to: case eok_ref_indirect: case eok_array_to_pointer: case eok_lvalue: case eok_lvalue_adjust: case eok_class_rvalue_adjust: case eok_unbox_lvalue: /* These operations are always implicit. Keep stripping. */ break; case eok_parens: /* Explicit, but irrelevant since precedence is not applied in binding operands to operators during a rescan. */ break; default: /* Something else. Stop stripping. */ goto end_of_loop; } /* switch */ expr = expr->variant.operation.operands; } /* if */ } else if (is_constant_node(expr)) { a_constant_ptr con = node_constant(expr); an_expr_node_ptr next_expr = NULL; if (constant_is(con, ck_template_param) && !con->variant.template_param .has_generic_cast_for_nontype_template_param && !con->variant.template_param.do_not_rescan) { if (con->variant.template_param.kind == (a_template_param_constant_kind)tpck_expression) { next_expr = expr_node_from_tpck_expression(con); } /* if */ } /* if */ if (next_expr != NULL) { expr = next_expr; } else { goto end_of_loop; } /* if */ } else if (expr->kind == (an_expr_node_kind)enk_temp_init) { a_dynamic_init_ptr dip = expr->variant.init.dynamic_init; /* Explicit casts are retained. */ if (!is_generated_dynamic_init(dip)) goto end_of_loop; /* Constant dynamic inits can't be stripped because there's no underlying expression to return. */ if (dip->kind == (a_dynamic_init_kind)dik_constant) { check_assertion(expr->extra.rescan_info != NULL); goto end_of_loop; } /* if */ /* Anything else is implicit and stripped. */ expr = arg_list_from_dyn_init(dip); } else { break; } /* if */ /* Remember the last rescan info block we encounter. */ if (periep != NULL && expr->extra.rescan_info != NULL) { *periep = expr->extra.rescan_info; } /* if */ } /* for */ end_of_loop: return expr; } /* strip_implicit_operations_for_rescan */ an_expr_rescan_info_entry_ptr get_expr_rescan_info( an_expr_node_ptr expr, an_expr_rescan_info_entry *rescan_info) /* Extract the rescan_info pointer from the indicated expression node and return it. If the node does not have recorded rescan information, see if there is default information available in the expression stack from an enclosing expression with rescan info, use that to build default rescan information (containing basically just positions) in *rescan_info, and return a pointer to that. If default information is needed and rescan_info is NULL or no default information is available, abort. */ { an_expr_rescan_info_entry_ptr eriep = expr->extra.rescan_info, default_eriep; if (eriep == NULL) { /* Generate default rudimentary rescan information from the information on the nearest enclosing expression that has it. */ check_assertion_str(rescan_info != NULL, "missing rescan info"); check_assertion_str(expr_stack != NULL && expr_stack->default_rescan_info != NULL, "missing default rescan info"); eriep = rescan_info; default_eriep = expr_stack->default_rescan_info; clear_expr_rescan_info_entry(eriep); copy_operand_position(&default_eriep->saved_operand, &eriep->saved_operand); eriep->expression_kind = default_eriep->expression_kind; } /* if */ return eriep; } /* get_expr_rescan_info */ void make_rescan_operand_full( an_expr_node_ptr expr, a_rescan_control_block *rcblock, a_local_expr_options_set local_options, an_operand *operand, an_operand *bound_function_selector) /* As part of redoing semantic analysis on an expression while doing template deduction, convert expr (an operand of the expression currently being processed) to an_operand form in *operand. rcblock provides context information for the deduction being done, e.g., the template argument list. local_options provides some options, in particular information about the immediate parent operator. Note that the process here includes making a copy, so the operand returned will never use any part of the original expression, and the original expression is not modified. If bound_function_selector is non-NULL, the caller is willing to accept a bound function as the result, and the selector for that can be stored in *bound_function_selector. */ { an_expr_node_ptr orig_expr = expr; an_expr_node_ptr expr_copy = NULL; a_boolean rescanned_case = FALSE; a_boolean is_template_param_object = FALSE; a_boolean is_template_param_lvalue = FALSE; a_constant_ptr constant = local_constant(); a_constant_ptr alloc_con = NULL; an_expr_rescan_info_entry_ptr eriep; an_expr_rescan_info_entry rescan_info; /* Drop implicit operations like conversions, since they don't have a source counterpart. */ expr = strip_implicit_operations_for_rescan(expr, &eriep); if (eriep == NULL) { eriep = get_expr_rescan_info(expr, &rescan_info); } /* if */ if (rcblock->error_detected) { /* For speed, stop substituting if there was a deduction error on a previous operand. */ } else if (expr_is_rescannable(expr)) { /* Rescan the expression. Note that going this route rather than through copy_template_param_expr allows us to keep the rescanned expression in an_operand form rather than having to convert it to an expression and then back again. */ /* Pass orig_expr in case the rescan information is on one of the implicit operations stripped above. */ rescan_expr_with_substitution_internal(orig_expr, rcblock, local_options, operand, bound_function_selector, /*top_level_expr=*/FALSE); rescanned_case = TRUE; } else { /* Copy the expression with substitution. */ a_boolean copy_error = FALSE; expr_copy = copy_template_param_expr(expr, rcblock->template_arg_list, rcblock->template_param_list, (a_type_ptr)NULL, &eriep->saved_operand.position, rcblock->options, ©_error, rcblock->ctws_state, constant, &alloc_con); if (copy_error) { subst_fail(rcblock->error_detected); } /* if */ } /* if */ if (rcblock->error_detected) { make_error_operand(operand); copy_operand_position(&eriep->saved_operand, operand); } else { if (rescanned_case) { /* The operand was already built above. */ } else { if (expr_copy == NULL) { a_constant_ptr cp = alloc_con != NULL ? alloc_con : constant; if (is_constant_node(expr) && constant_is(node_constant(expr), ck_template_param) && tpck_is(node_constant(expr), tpck_param) && constant_is(cp, ck_aggregate)) { /* A C++20 nontype template parameter of class type has been replaced by its argument value. As when such a parameter is referenced in an instantiation (see make_sym_constant_operand), the result is an lvalue designating the template parameter object. That object is identified by the argument value itself, i.e., without the "const" that substitution adds to the value. */ if (cp != constant) { /* cp may be the template argument itself (if the argument value was produced by an earlier substitution), so it must not be modified. */ copy_constant(cp, constant); cp = constant; } /* if */ if (cp->variant.aggregate.added_const_for_template_param) { cp->type = remove_qualifiers(cp->type, TQ_CONST); cp->variant.aggregate.added_const_for_template_param = FALSE; } /* if */ make_template_param_object_operand(cp, operand, /*copy_constant=*/TRUE); is_template_param_object = TRUE; } else { /* The result is a constant, so return a constant operand. */ make_constant_operand(cp, operand); if (constant_is(cp, ck_template_param) && is_operation_node(orig_expr) && node_operator_is(orig_expr, eok_lvalue) && orig_expr->variant.operation.operands == expr) { /* A reference to a C++20 nontype template parameter of class type that is still a template parameter after substitution (e.g., when an enclosing template is instantiated): Keep it an lvalue, as it was originally (see make_sym_constant_operand), so that it can later be identified as an id-expression designating the template parameter object. */ change_template_param_constant_operand_to_lvalue( operand, !is_template_dependent_type(cp->type)); is_template_param_lvalue = TRUE; } /* if */ } /* if */ } else { /* Return an expression operand. */ make_lvalue_or_rvalue_expression_operand(expr_copy, operand); } /* if */ if (is_any_reference_type(operand->type)) { add_reference_indirection(operand); } /* if */ } /* if */ /* The information in the rescan info was saved for this moment, when we can use it to restore operand information that would not otherwise survive in the IL. Do that now. */ if (operand->is_operand_of_address_of) { /* For indefinite functions with an implied "&", restore just the position information, because the original operand may have been for a "&" operator, and wouldn't have all the identifier-form flags. */ check_assertion(is_indefinite_function_operand(operand)); copy_operand_position(&eriep->saved_operand, operand); } else { /* Normal case: restore everything. */ restore_operand_info_from_expr_rescan_info_entry(operand, eriep); } /* if */ if ((is_template_param_object || is_template_param_lvalue) && orig_expr->extra.rescan_info != NULL) { /* The rescan information on the template parameter constant was saved before the reference was identified as an id-expression (possibly parenthesized). That is recorded on the node that makes the reference an lvalue (see make_sym_constant_operand), and it matters, e.g., for decltype. */ restore_operand_id_details(operand, &orig_expr->extra.rescan_info->saved_operand); } /* if */ } /* if */ release_local_constant(&constant); } /* make_rescan_operand_full */ void make_rescan_operand(an_expr_node_ptr expr, a_rescan_control_block *rcblock, an_operand *operand) /* Interface to make_rescan_operand_full for the usual case where a bound function is not allowed. */ { make_rescan_operand_full(expr, rcblock, EOPT_NO_OPTIONS, operand, (an_operand *)NULL); } /* make_rescan_operand */ static void get_rescan_operator_positions( an_expr_rescan_info_entry_ptr eriep, a_source_position *operator_position, a_token_sequence_number *operator_tok_seq_number, a_source_position *operator_position_2) /* Extract position information from the rescan info pointed to by eriep and return it in operator_position, operator_tok_seq_number, and (if it's non-NULL) operator_position_2. */ { *operator_position = eriep->operator_position; *operator_tok_seq_number = eriep->operator_token_sequence_number; if (operator_position_2 != NULL) { *operator_position_2 = eriep->secondary_operator_position; } /* if */ } /* get_rescan_operator_positions */ static void check_incomplete_return_type_allowed_for_rescan( an_expr_node_ptr expr) /* The operands of expr have been extracted ("rescanned"), and semantic analysis is about to be redone for the operation represented by expr itself. If this operation is the top-level operation of a decltype operation, set expr_stack->allow_call_with_incomplete_return_type to TRUE/ */ { if (expr_stack->is_type_operator_arg_expression) { if (expr == decltype_rescan_operand) { expr_stack->allow_call_with_incomplete_return_type = TRUE; } /* if */ } /* if */ } /* check_incomplete_return_type_allowed_for_rescan */ void make_rescan_operands(a_rescan_control_block *rcblock, an_operand *operand_1, an_operand *operand_2, an_operand *operand_3, a_source_position *operator_position, a_token_sequence_number *operator_tok_seq_number, a_source_position *operator_position_2) /* As part of redoing semantic analysis on an expression while doing template deduction, extract the operands of the expression given by rcblock->expr (an enk_operation or enk_builtin_operation node) and return them as operand_1, operand_2, and operand_3 (unneeded operands are set to NULL; the caller can use that also to rescan only selected operands, e.g., skip operand 1 but rescan operands 2 and 3). Also return the operator position and operator token sequence number in *operator_position and *operator_tok_seq_number. If operator_position_2 is non-NULL, return a secondary operator position (if any) in *operator_position_2. rcblock also gives context information for the template deduction being done, e.g., the template argument list being tried. */ { an_expr_node_ptr expr = rcblock->expr, op1, op2, op3; an_expr_rescan_info_entry_ptr eriep; an_expr_rescan_info_entry rescan_info; a_boolean is_operation; a_boolean allow_call_with_incomplete_return_type; check_assertion(expr != NULL); /* If a call is embedded in an operation, it cannot be allowed to have an incomplete return type, unless that operation is a comma operation and call is the second operand (see below). */ allow_call_with_incomplete_return_type = expr_stack->allow_call_with_incomplete_return_type; expr_stack->allow_call_with_incomplete_return_type = FALSE; is_operation = is_operation_node(expr); check_assertion(is_operation || expr->kind == (an_expr_node_kind)enk_builtin_operation); eriep = get_expr_rescan_info(expr, &rescan_info); op1 = is_operation ? expr->variant.operation.operands : expr->variant.builtin_operation.operands; if (operand_1 != NULL) { a_local_expr_options_set local_options = EOPT_NO_OPTIONS; if (is_operation && node_operator_is(expr, eok_address_of)) { local_options |= EOPT_OPERAND_OF_ADDRESS_OF; } /* if */ make_rescan_operand_full(op1, rcblock, local_options, operand_1, (an_operand *)NULL); } /* if */ op2 = op1->next; if (op2 != NULL) { if (is_operation && node_operator_is(expr, eok_comma) && allow_call_with_incomplete_return_type) { expr_stack->allow_call_with_incomplete_return_type = TRUE; } /* if */ if (operand_2 != NULL) make_rescan_operand(op2, rcblock, operand_2); expr_stack->allow_call_with_incomplete_return_type = FALSE; op3 = op2->next; if (op3 != NULL) { if (operand_3 != NULL) make_rescan_operand(op3, rcblock, operand_3); check_assertion(op3->next == NULL); } /* if */ } /* if */ get_rescan_operator_positions(eriep, operator_position, operator_tok_seq_number, operator_position_2); check_incomplete_return_type_allowed_for_rescan(expr); } /* make_rescan_operands */ void make_call_rescan_operands( a_rescan_control_block *rcblock, an_operand *operand, an_operand *bound_function_selector, a_source_position *operator_position, a_token_sequence_number *operator_tok_seq_number, a_source_position *closing_paren_position) /* As part of redoing semantic analysis on an expression while doing template deduction, extract the operands of the call expression given by rcblock->expr and return the function to call as *operand, and the bound function selector (if any) as *bound_function_selector. Also return the operator position and operator token sequence number, and the position of the closing ")", in *operator_position, *operator_tok_seq_number, and *closing_paren_position. rcblock also gives context information for the template deduction being done, e.g., the template argument list being tried. rcblock->argument_list is set to the start of the argument list for the call, to be converted to operand form later. */ { an_expr_node_ptr expr = rcblock->expr, op1, args; an_expr_rescan_info_entry_ptr eriep; check_assertion(expr != NULL && is_operation_node(expr)); /* We pass NULL for the second argument because we want to require explicit rescan information on all calls. */ eriep = get_expr_rescan_info(expr, (an_expr_rescan_info_entry *)NULL); op1 = expr->variant.operation.operands; make_rescan_operand_full(op1, rcblock, EOPT_CALL_RESCAN, operand, bound_function_selector); if (rcblock->error_detected) goto done; args = op1->next; /* A call like p->f(x) where the p->f part was treated as a static selection during prototype instantiation (because we didn't know what function f would be selected) might on a rescan produce a bound function for the first operand. If that doesn't happen, then we might still have a call we did classify as having a selector, and for those we have to extract the selector from the second operand and bind it to the first operand. */ if (operand->bound_function) { check_assertion(node_operator_is(expr, eok_call)); } else { if (!node_operator_is(expr, eok_call) && !node_operator_is(expr, eok_subscript)) { /* For calls other than eok_call (and eok_subscript in C++23), rescan the selector and bind it to the function operand. */ rescan_selector_of_call(rcblock, operand, bound_function_selector); args = args->next; } /* if */ } /* if */ rcblock->argument_list = args; get_rescan_operator_positions(eriep, operator_position, operator_tok_seq_number, closing_paren_position); check_incomplete_return_type_allowed_for_rescan(expr); done:; } /* make_call_rescan_operands */ void make_selection_rescan_operands( a_rescan_control_block *rcblock, an_operand *operand_1, a_boolean call_rescan_case, a_boolean offsetof_case, a_source_position *operator_position, a_token_sequence_number *operator_tok_seq_number) /* As part of redoing semantic analysis on an expression while doing template deduction, extract the operands of the expression given by rcblock->expr (a selection node) and return the first as operand_1. (The second operand will be handled later.) Also return the operator position and operator token sequence number in *operator_position and *operator_tok_seq_number. If call_rescan_case is TRUE and rcblock->expr is a call node, it is a member call with an included implied selection; pick up the second operand of the call (the selector) and return it as operand_1. rcblock also gives context information for the template deduction being done, e.g., the template argument list being tried. offsetof_case is TRUE if we are handling a field selection in a __builtin_offsetof. */ { an_expr_node_ptr expr = rcblock->expr, op1, sel_op; an_expr_rescan_info_entry_ptr eriep; an_expr_rescan_info_entry rescan_info; a_local_expr_options_set local_options = EOPT_NO_OPTIONS; check_assertion(expr != NULL && is_operation_node(expr)); eriep = get_expr_rescan_info(expr, &rescan_info); op1 = expr->variant.operation.operands; if (call_rescan_case && is_call_node(expr)) { sel_op = op1->next; } else { sel_op = op1; } /* if */ if (offsetof_case) local_options |= EOPT_OPERAND_OF_OFFSETOF; make_rescan_operand_full(sel_op, rcblock, local_options, operand_1, (an_operand *)NULL); get_rescan_operator_positions(eriep, operator_position, operator_tok_seq_number, (a_source_position *)NULL); } /* make_selection_rescan_operands */ a_boolean is_uuidof_expr(an_expr_node_ptr expr, /* Defaulted: */ a_boolean *p_is_type, /* Defaulted: */ an_expr_node_ptr *p_op_expr, /* Defaulted: */ a_type_ptr *p_type) /* Return TRUE if the indicated expression is the IL that represents a Microsoft __uuidof construct, i.e., a "*" operator on top of a constant that gives the address of a GUID for a uuidof. When TRUE is returned, and if p_is_type, p_op_expr, and p_type are non-NULL (they default to NULL), *p_is_type is returned TRUE to indicate the __uuidof is applied to a type (which is returned in *p_type) or FALSE to indicate it is applied to an expression (which is returned in *p_op_expr). */ { a_boolean is_uuidof = FALSE, is_type = FALSE; a_type *type = NULL; an_expr_node *op_expr = NULL; if (is_operation_node(expr) && expr->compiler_generated && node_operator_is(expr, eok_indirect)) { /* The expression has a generated "*" on top. Look underneath to see if we have a uuidof address. */ expr = expr->variant.operation.operands; if (is_constant_node(expr)) { a_constant_ptr con = node_constant(expr); if (constant_is(con, ck_address)) { if (con->variant.address.kind == abk_uuidof) { is_uuidof = TRUE; is_type = TRUE; type = con->variant.address.variant.type; } /* if */ } else if (constant_is(con, ck_template_param)) { if (con->variant.template_param.kind == tpck_uuidof) { is_uuidof = TRUE; op_expr = generic_sizeof_arg_expr(con); is_type = (op_expr == NULL); if (is_type) { type = con->variant.template_param.variant.templ_sizeof.type; } /* if */ } /* if */ } /* if */ } /* if */ } /* if */ if (p_is_type != NULL) *p_is_type = is_type; if (p_type != NULL) *p_type = type; if (p_op_expr != NULL) *p_op_expr = op_expr; return is_uuidof; } /* is_uuidof_expr */ a_type_ptr do_type_substitution_for_rescan( a_type_ptr type, a_rescan_control_block *rcblock, an_expr_rescan_info_entry_ptr eriep) /* As part of rescanning an expression for template deduction, do type substitution on the type "type" using the template arguments etc. given by rcblock, and with additional information from *eriep, and return the substituted type, or an error indication in rcblock. */ { a_type_ptr new_type; a_boolean copy_error = FALSE; a_ctws_options_set ctws_options = CTWS_NON_CONSTANT_EXPR; ctws_options |= (rcblock->options & (CTWS_PRESERVE_DEDUCED_PACKS | CTWS_PARTIAL_ARG_LIST_OKAY | CTWS_MAY_BE_RESCANNED)); new_type = copy_type_with_substitution(type, rcblock->template_arg_list, rcblock->template_param_list, &eriep->saved_operand.position, ctws_options, ©_error, rcblock->ctws_state); if (copy_error) { subst_fail(rcblock->error_detected); } /* if */ return new_type; } /* do_type_substitution_for_rescan */ static a_template_ptr do_template_substitution_for_rescan( a_template_ptr templ, a_rescan_control_block *rcblock, an_expr_rescan_info_entry_ptr eriep) /* As part of rescanning an expression for template deduction, do template substitution on the template "templ" using the template arguments, etc., given by rcblock as well as additional information from eriep. Return the substituted template or an error indication in rcblock. */ { a_template_ptr new_templ; a_boolean copy_error = FALSE; a_ctws_options_set ctws_options = CTWS_NON_CONSTANT_EXPR; ctws_options |= (rcblock->options & (CTWS_PRESERVE_DEDUCED_PACKS | CTWS_PARTIAL_ARG_LIST_OKAY | CTWS_MAY_BE_RESCANNED)); new_templ = copy_template_with_substitution(templ, rcblock->template_arg_list, rcblock->template_param_list, &eriep->saved_operand.position, ctws_options, ©_error, rcblock->ctws_state); if (copy_error) { subst_fail(rcblock->error_detected); } /* if */ return new_templ; } /* do_template_substitution_for_rescan */ void make_sizeof_et_al_rescan_operands( a_rescan_control_block *rcblock, a_boolean *p_is_type, an_operand *operand, a_type_ptr *p_type, a_source_position *operator_position, a_token_sequence_number *operator_tok_seq_number, a_source_position *type_position) /* As part of redoing semantic analysis on an expression while doing template deduction, extract the operands of the expression given by rcblock->expr (a sizeof, alignof, uuidof, typeid, or noexcept node) and: 1) Set *p_is_type indicating whether the operator is applied to a type (TRUE) or an expression (FALSE). 2) For the expression case, set *operand to the rescanned expression. 3) For the type case, set *p_type to the substituted type and *type_position to the source position of the type. Also return the operator position and operator token sequence number in *operator_position and *operator_tok_seq_number. rcblock also gives context information for the template deduction being done, e.g., the template argument list being tried. */ { a_boolean is_type; a_type_ptr type; an_expr_node_ptr expr = rcblock->expr, op_expr; an_expr_rescan_info_entry_ptr eriep; an_expr_rescan_info_entry rescan_info; check_assertion(expr != NULL); eriep = get_expr_rescan_info(expr, &rescan_info); if (is_uuidof_expr(expr, &is_type, &op_expr, &type)) { /* The expression represents a __uuidof. */ } else if (expr->kind == (an_expr_node_kind)enk_sizeof || expr->kind == (an_expr_node_kind)enk_datasizeof || expr->kind == (an_expr_node_kind)enk_alignof) { /* sizeof or alignof in expression form. */ is_type = expr->variant.sizeof_info.is_type; if (is_type) { type = expr->variant.sizeof_info.variant.type; } else { op_expr = expr->variant.sizeof_info.variant.expr; } /* if */ } else if (expr->kind == (an_expr_node_kind)enk_typeid) { /* typeid in expression form. */ an_expr_node_ptr opnds = expr->variant.typeid_info.type_with_opt_expr; op_expr = opnds->next; is_type = (op_expr == NULL); if (is_type) { check_assertion(node_is(opnds, enk_type_operand)); type = opnds->variant.type_operand.type; } /* if */ } else if (is_operation_node(expr) && node_operator_is(expr, eok_noexcept)) { /* noexcept in expression form. */ op_expr = expr->variant.operation.operands; } else if (is_constant_node(expr)) { /* tpck_sizeof, tpck_alignof, tpck_typeid, or tpck_noexcept constant form. tpck_uuidof gets handled above and doesn't get here. */ a_constant_ptr con = node_constant(expr); check_assertion(constant_is(con, ck_template_param) && (tpck_is(con, tpck_sizeof) || tpck_is(con, tpck_datasizeof) || tpck_is(con, tpck_alignof) || tpck_is(con, tpck_typeid) || tpck_is(con, tpck_noexcept))); op_expr = generic_sizeof_arg_expr(con); is_type = (op_expr == NULL); if (is_type) { type = con->variant.template_param.variant.templ_sizeof.type; } /* if */ } else { unexpected_condition(); } /* if */ *p_is_type = is_type; if (is_type) { /* Type case. Do substitution on the type. */ if (type == NULL) { /* For __uuidof(0), the type is NULL. */ *p_type = NULL; } else { *p_type = do_type_substitution_for_rescan(type, rcblock, eriep); } /* if */ } else { /* Expression case. Rescan the operand. */ make_rescan_operand(op_expr, rcblock, operand); *p_type = NULL; } /* if */ get_rescan_operator_positions(eriep, operator_position, operator_tok_seq_number, type_position); } /* make_sizeof_et_al_rescan_operands */ an_expr_node_ptr arg_list_from_dyn_init(a_dynamic_init_ptr dip) /* Return the effective argument list of the indicated dynamic initialization, to be used for expression rescan purposes. The effective argument list is the initializer expression or the arguments of the constructor call. */ { a_constant_ptr con; an_expr_node_ptr args = NULL; top_of_routine: switch (dip->kind) { case dik_none: case dik_zero: /* No arguments in these cases. */ args = NULL; break; case dik_expression: case dik_class_result_via_ctor: /* The single expression is the argument. */ args = dip->variant.expression; break; case dik_constructor: /* The constructor call argument list is the argument list. */ args = dip->variant.constructor.args; break; case dik_nonconstant_aggregate: /* This is okay if it's a repeated initialization for an array, e.g., a default constructor call for each element. */ con = dip->variant.constant.ptr; check_assertion(con->kind == (a_constant_repr_kind)ck_aggregate); con = con->variant.aggregate.first_constant; check_assertion(con->next == NULL); if (con->kind == (a_constant_repr_kind)ck_init_repeat && con->variant.init_repeat.constant->kind == (a_constant_repr_kind)ck_dynamic_init) { dip = con->variant.init_repeat.constant->variant.dynamic_init.ptr; goto top_of_routine; } /* if */ unexpected_condition_str("unexpected dynamic init kind"); case dik_bitwise_copy: if (dip->variant.bitwise_copy.source != NULL) { args = dip->variant.bitwise_copy.source; } else { unexpected_condition_str("unexpected dynamic init kind"); } /* if */ break; case dik_constant: /* This can happen if the constant was folded. */ args = dip->variant.constant.ptr->expr; break; default: unexpected_condition_str("unexpected dynamic init kind"); } /* switch */ return args; } /* arg_list_from_dyn_init */ void make_cast_rescan_operands(a_rescan_control_block *rcblock, a_dynamic_init_ptr dip, a_source_position *start_position, a_type_ptr *cast_type, a_source_position *type_position, an_init_component_ptr *braced_init_list, a_dynamic_init_ptr *actual_dip, an_operand *operand, an_operand *bound_function_selector) /* As part of redoing semantic analysis on an expression while doing template deduction, extract the operand of the cast expression given by rcblock->expr and return it in *operand. (If dip != NULL, use that as the cast instead of rcblock->expr.) Also return the type cast to in *cast_type, the starting position of the cast in *start_position, and the position of the type in the cast in *type_position. If bound_function_selector is non-NULL, the caller is willing to accept a bound function, and *bound_function_selector can be set to the selector part of that. If rcblock->operator_token is tok_typename, indicating a functional-notation type conversion (or an old-style cast or compound literal), *operand and *bound_function_selector are not used; the argument list for the cast is returned in rcblock->argument_list instead (there might be more than one argument). If the cast was initialized by a braced-init-list (including for compound literals), return a copied/substituted list in *braced_init_list (a single entry that is a braced list) instead of returning *operand and *bound_function_selector. If actual_dip != NULL, *actual_dip is set to point to the actual dynamic init that underlies the cast, or NULL if there isn't one. */ { an_expr_node_ptr expr = rcblock->expr, op1 = NULL; an_expr_rescan_info_entry_ptr eriep; a_token_sequence_number operator_tok_seq_number; a_type_ptr orig_type; if (braced_init_list != NULL) *braced_init_list = NULL; if (actual_dip != NULL) *actual_dip = NULL; if (dip != NULL) { /* The cast is specified by a dynamic initialization (dip), not an expression. */ eriep = dip->rescan_info; orig_type = eriep->type; } else { /* The cast is specified by an expression. */ check_assertion(expr != NULL && (is_cast_operation_node(expr) || expr->kind == (an_expr_node_kind)enk_temp_init)); /* We pass NULL for the second argument because we want to require explicit rescan information on all casts. */ eriep = get_expr_rescan_info(expr, (an_expr_rescan_info_entry *)NULL); if (expr->kind == (an_expr_node_kind)enk_temp_init) { dip = expr->variant.init.dynamic_init; check_assertion(dip != NULL); /* Make Coverity analysis happy. */ /* For an expression S{} in a context requiring a constant "bool", eriep->type will represent "bool". However, at instantiation time there are really up to three conversions in play here: (1) The conversion of "{}" to S, (2) a call to a user-defined conversion function, and (3) a standard conversion from the result of that user-defined conversion to bool. The type to substitute at this level would be S, which is expr->type. However, for error recovery purposes, eriep->type works better if expr->type cannot be a class type. */ if (is_class_struct_union_type(expr->type) || is_template_param_type(expr->type)) { orig_type = expr->type; } else { orig_type = eriep->type; } /* if */ } else { op1 = expr->variant.operation.operands; orig_type = eriep->type; } /* if */ } /* if */ /* Determine the result type for the cast. */ check_assertion(eriep != NULL && eriep->type != NULL); *cast_type = do_type_substitution_for_rescan(orig_type, rcblock, eriep); if (actual_dip != NULL) *actual_dip = dip; if (dip != NULL && dip->rescan_info != NULL && is_braced_init_list_operand(&dip->rescan_info->saved_operand)) { /* A braced-init-list was saved with the dynamic init for the rescan. Use it. */ check_assertion(braced_init_list != NULL); *braced_init_list = rescan_init_component( dip->rescan_info->saved_operand.variant.braced_init_list, rcblock); } else { /* Not a cast with a saved braced-init-list. */ if (dip != NULL) { op1 = arg_list_from_dyn_init(dip); } /* if */ if (rcblock->operator_token == tok_typename) { /* Functional-notation cast (or old-style cast, including non-aggregate compound literals). */ if (expr != NULL && expr->is_brace_notation_cast) { /* The original cast was of the form T{...} but no braced-init-list was saved. Reconstruct one. */ an_arg_list_elem *braced_list; braced_list = alloc_init_component((an_init_component_kind)ick_braced); braced_list->variant.braced.list = rescan_expr_list(op1, rcblock); check_assertion(braced_init_list != NULL); *braced_init_list = braced_list; } else { /* Return the argument list via rcblock->argument_list. There may be more than one argument, or zero. */ rcblock->argument_list = op1; } /* if */ } else { /* Not a functional-notation cast, e.g., something like static_cast. Return the single argument expression via *operand and *bound_function_selector. */ a_local_expr_options_set options = EOPT_OPERAND_OF_CAST; make_rescan_operand_full(op1, rcblock, options, operand, bound_function_selector); } /* if */ } /* if */ get_rescan_operator_positions(eriep, start_position, &operator_tok_seq_number, type_position); } /* make_cast_rescan_operands */ void make_new_delete_rescan_operands( a_rescan_control_block *rcblock, a_new_delete_supplement_ptr *ndsp, a_source_position *start_position, a_type_ptr *type, a_source_position *type_position) /* As part of redoing semantic analysis on an expression while doing template deduction, extract and return information about the "new" or "delete" expression given by rcblock->expr. *ndsp is set to the new-delete supplement. *start_position is set to the starting position of the expression. If type is non-NULL, *type is set to the type of entity to be allocated. If type_position is non-NULL, *type_position is set to the position of the type in the expression. */ { an_expr_node_ptr expr = rcblock->expr; an_expr_rescan_info_entry_ptr eriep; a_token_sequence_number operator_tok_seq_number; check_assertion(expr != NULL); check_assertion(expr->kind == (an_expr_node_kind)enk_new_delete); /* We pass NULL for the second argument because we want to require explicit rescan information on all news/deletes. */ eriep = get_expr_rescan_info(expr, (an_expr_rescan_info_entry *)NULL); *ndsp = expr->variant.new_delete; if (type != NULL) { check_assertion(eriep->type != NULL); *type = do_type_substitution_for_rescan(eriep->type, rcblock, eriep); } /* if */ get_rescan_operator_positions(eriep, start_position, &operator_tok_seq_number, type_position); } /* make_new_delete_rescan_operands */ #if MICROSOFT_EXTENSIONS_ALLOWED void make_gcnew_rescan_operands(a_rescan_control_block *rcblock, a_gcnew_supplement_ptr *gsp, a_source_position *start_position, a_type_ptr *type, a_source_position *type_position) /* As part of redoing semantic analysis on an expression while doing template deduction, extract and return information about the "gcnew" expression given by rcblock->expr. *gsp is set to the gcnew supplement. *start_position is set to the starting position of the expression. If type is non-NULL, *type is set to the type of entity to be allocated. If type_position is non-NULL, *type_position is set to the position of the type in the expression. */ { an_expr_node_ptr expr = rcblock->expr; an_expr_rescan_info_entry_ptr eriep; a_token_sequence_number operator_tok_seq_number; check_assertion(expr != NULL); check_assertion(expr->kind == (an_expr_node_kind)enk_gcnew); /* We pass NULL for the second argument because we want to require explicit rescan information on all gcnews. */ eriep = get_expr_rescan_info(expr, (an_expr_rescan_info_entry *)NULL); *gsp = expr->variant.gcnew_info; if (type != NULL) { check_assertion(eriep->type != NULL); *type = do_type_substitution_for_rescan(eriep->type, rcblock, eriep); } /* if */ get_rescan_operator_positions(eriep, start_position, &operator_tok_seq_number, type_position); } /* make_gcnew_rescan_operands */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ void make_throw_rescan_operands(a_rescan_control_block *rcblock, a_source_position *start_position, an_operand *operand, a_boolean *expr_present) /* As part of redoing semantic analysis on an expression while doing template deduction, extract the operand of the throw expression given by rcblock->expr and return it in *operand. *start_position is set to the starting position of the expression. *expr_present is returned FALSE for a rethrow, in which case *operand is not set. */ { an_expr_node_ptr expr = rcblock->expr; an_expr_rescan_info_entry_ptr eriep; a_token_sequence_number operator_tok_seq_number; check_assertion(expr != NULL); check_assertion(expr->kind == (an_expr_node_kind)enk_throw); /* We pass NULL for the second argument because we want to require explicit rescan information on all throws. */ eriep = get_expr_rescan_info(expr, (an_expr_rescan_info_entry *)NULL); *expr_present = (expr->variant.throw_info != NULL); if (*expr_present) { /* Rescan the operand, substituting template arguments. */ rescan_dynamic_init_with_substitution( expr->variant.throw_info->dynamic_init, rcblock, operand); } /* if */ get_rescan_operator_positions(eriep, start_position, &operator_tok_seq_number, (a_source_position *)NULL); } /* make_throw_rescan_operands */ void make_type_operand_rescan_type(a_rescan_control_block *rcblock, a_type_ptr *type, a_source_position *type_position) /* rcblock->argument_list points to a type-operand argument for a builtin operation (like a type trait test). Get the type, do substitution on it (using information from rcblock), and return the substituted type in *type. Also return the source position of the operand in *type_position. */ { an_expr_node_ptr expr = rcblock->argument_list; an_expr_rescan_info_entry_ptr eriep; check_assertion(expr != NULL); check_assertion(expr->kind == (an_expr_node_kind)enk_type_operand); /* We pass NULL for the second argument because we want to require explicit rescan information on all type operands. */ eriep = get_expr_rescan_info(expr, (an_expr_rescan_info_entry *)NULL); *type = do_type_substitution_for_rescan(expr->variant.type_operand.type, rcblock, eriep); *type_position = eriep->saved_operand.position; } /* make_type_operand_rescan_type */ void make_template_name_rescan_template(a_rescan_control_block *rcblock, a_template_ptr *templ, a_source_position *templ_position) /* rcblock->argument_list points to a template-name argument for a builtin operation (like a type trait test). Get the template, do substitution on it (using information from rcblock), and return the substituted template in *templ. Also return the source position of the operand in *templ_position. */ { an_expr_node_ptr expr = rcblock->argument_list; an_expr_rescan_info_entry_ptr eriep; check_assertion(expr != NULL); check_assertion(expr->kind == enk_template_name); /* We pass NULL for the second argument because we want to require explicit rescan information on all type operands. */ eriep = get_expr_rescan_info(expr, (an_expr_rescan_info_entry *)NULL); *templ = do_template_substitution_for_rescan(expr->variant.template_name, rcblock, eriep); *templ_position = eriep->saved_operand.position; } /* make_template_name_rescan_template */ an_arg_list_elem_ptr rescan_expr_as_arg_list_elem( an_expr_node_ptr expr, a_rescan_control_block *rcblock) /* Rescan the given expression, producing an argument list element as the result. rcblock gives the rescan control information, including information on the template parameter substitutions to be done. */ { an_arg_list_elem_ptr alep; an_operand rescanned_operand; make_rescan_operand(expr, rcblock, &rescanned_operand); if (is_braced_init_list_operand(&rescanned_operand)) { /* The operand returned is for a braced-init-list, so just use the entry contained therein. We can do that because we just created the copy, so we know it's not shared. */ alep = rescanned_operand.variant.braced_init_list; rescanned_operand.variant.braced_init_list = NULL; } else { alep = alloc_arg_list_elem_for_operand(&rescanned_operand); } /* if */ return alep; } /* rescan_expr_as_arg_list_elem */ static an_init_component_ptr rescan_init_component_list( an_init_component_ptr list_icp, a_rescan_control_block *rcblock) /* Rescan the given init-component list, producing a list of copied/substituted init component entries as a result. rcblock gives the rescan control information, including information on the template parameter substitutions to be done. The list is an initialization list, not an argument list, so it may include designators. */ { an_init_component_ptr copy_list_icp = NULL, end_copy_list_icp = NULL; an_init_component_ptr icp, copy_icp; for (icp = list_icp; icp != NULL; icp = next_elem(icp)) { a_pack_expansion_descr_ptr pedep = icp->pack_expansion_descr; a_pack_expansion_stack_entry_ptr pesep; a_boolean any_more, err; any_more = begin_rescan_pack_expansion_context( pedep, rcblock->template_param_list, rcblock->template_arg_list, &pesep, rcblock->options, rcblock->ctws_state, &err); /* Check if an error occurred (such as mismatched parameter pack lengths). */ if (err) subst_fail(rcblock->error_detected); while (any_more) { if (is_designator_component(icp)) { /* A designator just gets copied. Because the field designation is in terms of a name (symbol header pointer) it doesn't need to be substituted. Likewise the array designator just has an index number (or two), which isn't changed by substitution. */ copy_icp = alloc_init_component( (an_init_component_kind)ick_designator); copy_icp->variant.designator = icp->variant.designator; } else { /* Other kinds of entries; rescan in the usual way. */ copy_icp = rescan_init_component(icp, rcblock); } /* if */ /* Add copy_icp to the copy_list_icp list. */ if (copy_list_icp == NULL) { copy_list_icp = copy_icp; } else { append_elem(end_copy_list_icp, copy_icp); } /* if */ end_copy_list_icp = copy_icp; (void)end_potential_pack_expansion_context(pesep, /*is_declarator=*/FALSE); any_more = advance_to_next_pack_element(pesep); } /* while */ /* Preserve the pack if requested. */ if (rcblock->options & CTWS_PRESERVE_DEDUCED_PACKS && is_pack_expansion_component(icp)) { if (copy_list_icp == NULL) { copy_list_icp = icp; } else { append_elem(end_copy_list_icp, icp); } /* if */ end_copy_list_icp = icp; } /* if */ } /* for */ return copy_list_icp; } /* rescan_init_component_list */ an_init_component_ptr rescan_init_component(an_init_component_ptr icp, a_rescan_control_block *rcblock) /* Rescan the given init-component, producing a copied/substituted init component entry as a result. rcblock gives the rescan control information, including information on the template parameter substitutions to be done. */ { an_init_component_ptr copy_icp = NULL; if (is_expression_component(icp)) { /* Rescan a single expression. */ an_expr_node_ptr expr; an_operand *operand = operand_of_arg_list_elem(icp); a_boolean saved_possible_rescan_context = expr_stack->possible_rescan_context; if (is_template_dependent_context() && is_indefinite_function_operand(operand)) { conv_indefinite_function_to_unknown_dependent_function( operand, /*force_to_rvalue=*/FALSE); } else if (is_braced_init_list_operand(operand)) { /* Handled okay below by make_node_from_operand. */ } else { /* Give an error on other unusual things, e.g., symbol-for-member operands. */ eliminate_unusual_operand_kinds(operand); } /* if */ if (is_constant_operand(operand) && constant_is(&operand->variant.constant, ck_template_param) && tpck_is(&operand->variant.constant, tpck_expression) && (expr = expr_node_from_tpck_expression(&operand->variant.constant)) != NULL && expr->extra.rescan_info != NULL) { /* If the operand is just a tpck_expression constant wrapping an expression with associated rescan information, use the expression directly instead of wrapping the constant in an enk_constant node, because the latter can lead to memory region complications. */ } else { /* When calling make_node_from_operand, make sure possible_rescan_context is TRUE so that necessary rescan info is recorded in the node. */ expr_stack->possible_rescan_context = TRUE; expr = make_node_from_operand(operand); expr_stack->possible_rescan_context = saved_possible_rescan_context; } /* if */ copy_icp = rescan_expr_as_arg_list_elem(expr, rcblock); } else if (is_braced_init_component(icp)) { /* Rescan a brace-enclosed list of init-components. */ copy_icp = alloc_init_component((an_init_component_kind)ick_braced); copy_icp->contains_designator = icp->contains_designator; copy_icp->variant.braced = icp->variant.braced; copy_icp->variant.braced.list = rescan_init_component_list(icp->variant.braced.list, rcblock); } else { /* Designators are not expected at this level. They are handled inside rescan_init_component_list. */ unexpected_condition(); } /* if */ return copy_icp; } /* rescan_init_component */ an_expr_node_ptr alloc_node_for_constant_operand(an_operand *operand) /* Allocate an enk_constant expression node for the given constant operand, and return a pointer to it. */ { an_expr_node_ptr node; a_constant_ptr con; a_constant_ptr local_con = local_constant(); check_assertion(is_constant_operand(operand)); con = &operand->variant.constant; if (operand->name_reference_set && con->kind == (a_constant_repr_kind)ck_ptr_to_member) { /* Pointer-to-member constants have their own name_reference field. Transfer the name reference information to the allocated constant. */ a_source_correspondence_ptr obj; if (con->variant.ptr_to_member.is_function_ptr) { obj = (a_source_correspondence *)con->variant.ptr_to_member.variant.routine; } else { obj = (a_source_correspondence *)con->variant.ptr_to_member.variant.field; } /* if */ /* The object pointer is NULL for NULL pointers-to-members. */ if (obj != NULL) { copy_constant(con, local_con); local_con->variant.ptr_to_member.name_reference = find_allocated_name_reference(obj, &operand->name_reference); con = local_con; } /* if */ } /* if */ node = alloc_node_for_constant(con); copy_operand_position_to_expr(operand, node); if (is_an_lvalue(operand)) { node->is_lvalue = TRUE; node->type = operand->type; } /* if */ release_local_constant(&local_con); return node; } /* alloc_node_for_constant_operand */ void rewrite_captured_variable_access(an_operand *opnd) /* The given operand is an enk_variable expression operand (potentially with some cast-like operations on top) for a local variable outside of an enclosing lambda. Rewrite the expression in terms of the corresponding closure field selection. */ { an_expr_node_ptr node = opnd->variant.expression; if (is_error_node(node) || is_error_type(node->type)) { /* Leave the node unchanged. */ } else { a_lambda_capture_ptr lcp; a_field_ptr closure_field; an_expr_node_ptr sel_expr, vnode = node; a_boolean is_lvalue = node->is_lvalue, is_xvalue = node->is_xvalue; a_scope_depth depth_lambda = get_curr_lambda_depth(); a_lambda_ptr lambda = get_lambda_for_scope_depth(depth_lambda); a_variable_ptr vp; opnd->pending_capture = FALSE; while (!is_variable_node(vnode)) { check_assertion(vnode->compiler_generated && node_is(vnode, enk_operation)); if (node_operator_is(vnode, eok_points_to_field)) { /* This can happen when the operand is copied with a shared underlying expression and the expression has been rewritten already. No further rewrite is needed. */ goto done; } /* if */ vnode = vnode->variant.operation.operands; } /* if */ vp = node_variable(vnode); if ((vp->is_constexpr || vp->constant_valued) && expr_stack->consteval_call_need_not_fold) { /* If we are in a context that will never be evaluated at run time, we need not rewrite the captured variable. This simplifies constant evaluation of c in the example below: struct C { int val; constexpr int &operator[](int) const { return (int&)val; } }; int main() { constexpr C c{1}; [c]() { if constexpr (c[1]) {} }(); } */ goto done; } /* if */ lcp = find_lambda_capture(lambda, vp, (a_field*)NULL); closure_field = field_for_lambda_capture(lambda, lcp); sel_expr = make_selection_for_captured_variable(lcp, depth_lambda, /*is_lvalue=*/TRUE); set_expr_position(sel_expr, &node->position, &node->expr_range.end, &node->position); if (is_any_reference_type(closure_field->type)) { an_operand sel_opnd; make_glvalue_expression_operand(sel_expr, &sel_opnd); copy_operand_position(opnd, &sel_opnd); add_reference_indirection(&sel_opnd); sel_expr = make_node_from_operand(&sel_opnd); } /* if */ *node = *sel_expr; node->is_lvalue = is_lvalue; node->is_xvalue = is_xvalue; opnd->type = node->type; } /* if */ done:; } /* rewrite_captured_variable_access */ static an_expr_node_ptr extract_node_from_operand( an_operand *operand, a_boolean no_rewrite = FALSE) /* Extract an expression from an operand. If the operand contains a constant, allocate a constant node and copy the constant value to it. If the operand is an error operand, create an error node. If the operand is an expression, return the expression node. This routine does not preserve information from the operand as extra information added to the expression and should be used when the expression is either (a) going to be simply tested or traversed for some property, and not saved, or (b) going to be immediately put back into an_operand, with the original operand information restored via a call to restore_operand_details. Otherwise, use make_node_from_operand instead. By default, this rewrites enk_variable nodes that refer to potentially-captured variables as selections of the corresponding closure field (when applicable). If no_rewrite is TRUE (default is FALSE), that rewrite doesn't happen. */ { an_expr_node_ptr node = NULL; switch (operand->kind) { case ok_error: /* Make an error node. Its type will be error. */ node = error_node(); node->position = operand->position; break; case ok_expression: /* Just return the node in the operand. However, if the expression represents a captured variable, rewrite it as a closure field selection. */ if (operand->pending_capture && !no_rewrite) { rewrite_captured_variable_access(operand); } /* if */ node = operand->variant.expression; break; case ok_constant: /* Create a constant node and copy the constant in the operand to the node. */ node = alloc_node_for_constant_operand(operand); break; case ok_braced_init_list: node = make_braced_init_expr_from_arg_list_elem( operand->variant.braced_init_list); break; default: unexpected_condition_str( "extract_node_from_operand: converting unexpected operand kind"); } /* switch */ return node; } /* extract_node_from_operand */ an_expr_node_ptr make_node_from_operand(an_operand *operand, /* Defaulted: */ a_boolean no_rewrite) /* Return an expression node to represent the given operand, creating one if necessary. Extra information that is in the an_operand entry is in some cases preserved as extra information attached to the expression node; see extract_node_from_operand for an alternative that does not do that extra work. By default, this rewrites enk_variable nodes that refer to potentially-captured variables as selections of the corresponding closure field (when applicable). If no_rewrite is TRUE (default is FALSE), that rewrite doesn't happen. */ { an_expr_node_ptr node = extract_node_from_operand(operand, no_rewrite); if (operand->name_reference_set) { if (is_routine_node(node)) { node->variant.routine.name_reference = find_allocated_name_reference( &node_routine(node)->source_corresp, &operand->name_reference); } else if (is_variable_node(node)) { if (operand->name_reference.is_template_id) { a_name_reference_ptr nrp = &operand->name_reference; a_template_arg_ptr list1 = node_variable(node)->template_info ->template_arg_list; a_template_arg_ptr list2 = nrp->orig_template_arg_list; if (!record_form_of_name_reference || are_template_args_lexically_identical( list1, list2, nrp->num_template_arguments)) { /* As all explicitly specified template arguments are lexically identical to the ones recorded in the variable node, there is no need to add them to the name reference. */ nrp->orig_template_arg_list = NULL; } /* if */ } /* if */ node->variant.variable.name_reference = find_allocated_name_reference( &node_variable(node)->source_corresp, &operand->name_reference); } else if (is_constant_node(node)) { a_template_param_constant_kind kind; a_constant_ptr con = node_constant(node); if (is_enum_constant(con) || (constant_is(con, ck_template_param) && ((kind = con->variant.template_param.kind), (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 || kind == (a_template_param_constant_kind)tpck_address || kind == (a_template_param_constant_kind)tpck_template_ref)))) { node->variant.constant.name_reference = find_allocated_name_reference( &con->source_corresp, &operand->name_reference); } /* if */ } else if (node->kind == (an_expr_node_kind)enk_field) { node->variant.field.name_reference = find_allocated_name_reference( &node_field(node)->source_corresp, &operand->name_reference); } else if (is_operation_node(node)) { an_expr_operator_kind op = node->variant.operation.kind; if (op == (an_expr_operator_kind)eok_dot_vacuous_destructor_call || op == (an_expr_operator_kind)eok_points_to_vacuous_destructor_call) { /* For vacuous destructors, look for a destructor type in the name reference. If one is found, and the type is a named typeref or class type (i.e., not a built-in type), attach a name reference to the node. */ a_type_ptr dtor_name_type; dtor_name_type = operand->name_reference.variant.destructor_type; if (dtor_name_type != NULL && has_name(dtor_name_type)) { an_expr_node_ptr vacuous_dtor_node; a_memory_region_number region_to_switch_back_to; switch_to_scope_region(depth_scope_stack, ®ion_to_switch_back_to); vacuous_dtor_node = alloc_expr_node((an_expr_node_kind)enk_routine); vacuous_dtor_node->type = void_type(); vacuous_dtor_node->variant.routine.name_reference = find_allocated_name_reference(&dtor_name_type->source_corresp, &operand->name_reference); check_assertion(node->variant.operation.operands != NULL); node->variant.operation.operands->next = vacuous_dtor_node; switch_back_to_original_region(region_to_switch_back_to); } /* if */ } /* if */ } /* if */ } /* if */ if (expr_stack != NULL && expr_stack->possible_rescan_context) { an_expr_node_ptr preexisting_node = expr_node_from_operand(operand); /* For an expression that may be rescanned to do semantic analysis later for template deduction, save extra information from the operand. */ save_operand_info_in_expr_rescan_info_entry(operand, node); if (preexisting_node != NULL && preexisting_node != node && preexisting_node->extra.rescan_info == NULL) { /* Record the same rescan info on any other equivalent expression. This is particularly important for a ck_template_param constant that has an expression under it; we'd like the rescan information on that expression also. */ preexisting_node->extra.rescan_info = node->extra.rescan_info; } /* if */ } /* if */ return node; } /* make_node_from_operand */ void record_rescan_info_for_node(an_expr_node *node) /* If we are in an expression context that may need substitution and the given node has no rescan information yet, record that information now. */ { if (expr_stack->possible_rescan_context && node->extra.rescan_info == NULL && !is_error_node(node)) { /* Get rescan information recorded for this expression by going by way of an operand. */ an_operand operand; make_expression_operand(node, &operand); #if EXPENSIVE_CHECKING { an_expr_node_ptr result; result = make_node_from_operand(&operand); /* We should get the same expression back when we extract it, because we haven't really done anything. */ check_assertion(result == node); } #else /* !EXPENSIVE_CHECKING */ (void)make_node_from_operand(&operand); #endif /* EXPENSIVE_CHECKING */ } /* if */ } /* record_rescan_info_for_node */ an_expr_node_ptr make_node_from_operand_for_expr_list(an_operand *operand) /* Return an expression node to represent the given operand, creating one if necessary. The resulting expression will be linked into an expression list, e.g., an argument list for a call. (That is significant if the operand is a pack expansion, because we will set the is_pack_expansion flag in the expression now that we know it will be at the top level in the list.) */ { an_expr_node_ptr expr = make_node_from_operand(operand); if (operand->pack_expansion_descr != NULL) { expr->is_pack_expansion = TRUE; } /* if */ return expr; } /* make_node_from_operand_for_expr_list */ an_expr_node_ptr full_expr_from_operand(an_operand *opnd) /* Produce an IL representation of the given operand interpreted as a full expression. */ { an_expr_node *result; eliminate_unusual_operand_kinds(opnd); result = make_node_from_operand(opnd); return wrap_up_full_expression(result); } /* full_expr_from_operand */ void mark_expr_of_operand_as_pack_expansion_if_necessary(an_operand *operand) /* If the given operand is a variadic template pack expansion, mark its underlying expression (if any) as a pack expansion. This is called just before the expression is put on an expression list so that any implicit conversions have already been added to the operand. */ { if (operand->pack_expansion_descr != NULL) { an_expr_node_ptr expr = expr_node_from_operand(operand); if (expr != NULL) expr->is_pack_expansion = TRUE; } /* if */ } /* mark_expr_of_operand_as_pack_expansion_if_necessary */ void set_operand_name_reference_from_locator(an_operand *operand, a_symbol_locator *locator) /* Transfer the form-of-reference information (if any) from the given locator to the indicated operand. */ { if (!C_mode() && !is_error_operand(operand) && !expr_stack->template_deduction_context && record_name_references_in_context() && name_reference_needed_for_locator(locator)) { make_name_reference_from_locator(locator, &operand->name_reference); operand->name_reference_set = TRUE; } /* if */ } /* set_operand_name_reference_from_locator */ void set_operand_id_details_from_locator(an_operand *operand, a_symbol_locator *locator) /* Transfer any id-related information from the locator to the given operand. This includes in particular name reference information. */ { set_operand_name_reference_from_locator(operand, locator); operand->is_qualified_name = locator->is_qualified_name; if (operand->kind == (an_operand_kind)ok_indefinite_function || operand->kind == (an_operand_kind)ok_undefined_symbol #if MICROSOFT_EXTENSIONS_ALLOWED || operand->kind == (an_operand_kind)ok_property_ref #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ ) { operand->id_position = locator->source_position; } /* if */ } /* set_operand_id_details_from_locator */ static a_boolean constexpr_call_folding_should_be_done(void) /* Return TRUE if constexpr call or constructor folding should be done in the current context. */ { a_boolean fold = FALSE; /* Note that folding should always be done for noexcept operands since it affects the outcome of the operand. That is true even in template- dependent contexts because the operand may not be itself dependent. Generally speaking, we do not need to fold calls at this time in an ek_integral_constant or ek_template_arg context because the top-level expression will need to be successfully interpreted later on anyway. */ if (!expr_stack->suppress_constexpr_call_folding && ((curr_expr_is_evaluated() && ((!is_prototype_instantiation_or_expansion_context() && curr_expr_kind() >= (int)ek_template_arg) || scope_is(&scope_stack_top(), sck_template_declaration))) || (expr_stack->in_noexcept_operand_expression && (core_constant_expr_is_noexcept || gpp_version_is(<90000))))) { fold = TRUE; } /* if */ if (fold && innermost_function_scope != NULL) { a_routine_ptr curr_rp = current_routine_entry(); if (curr_rp->has_deducible_return_type && !curr_rp->has_deduced_return_type) { /* Speculative folding of a constexpr call instantiates the callee. That must not happen while this function's return type is still being deduced: a recursive call (for example a generic lambda invoked from a decltype(auto) member) would then diagnose the incomplete function. */ fold = FALSE; } /* if */ } /* if */ return fold; } /* constexpr_call_folding_should_be_done */ void force_operand_to_constant_if_possible_full( an_operand *operand, a_boolean is_constant_evaluated) /* If possible, force the indicated operand to be a constant. Specifically, if the operand is an addressing expression of pointer type whose address is constant, turn the operand into that constant. Also, in constexpr constant expressions, fold to a constant result. is_constant_evaluated is TRUE if calls to std::is_constant_evaluated should to "true". */ { a_constant_ptr con = local_constant(); an_operand orig_operand; if (!is_expression_operand(operand)) { /* Nothing to do. */ } else if (!is_a_prvalue(operand)) { /* Nothing to do. */ } else if (constexpr_enabled && (curr_expr_kind_is_const() || (expr_stack->favor_constant_result && !is_template_dependent_context())) && (is_constant_evaluated || constexpr_call_folding_should_be_done()) && fold_constexpr_expr(operand->variant.expression, con, is_constant_evaluated, /*force_prvalue=*/FALSE)) { /* With constexpr enabled, the expression can be folded to a constant. */ orig_operand = *operand; #if MICROSOFT_EXTENSIONS_ALLOWED if (microsoft_mode && con->null_pointer_constant_ruled_out && cmplit_integer_constant(con, (a_host_large_integer)0) == 0) { /* Microsoft compilers accept a broader range of "null pointer" forms. After folding, restore the null pointer flag if needed. */ a_boolean is_nullptr_constant; a_constant_ptr null_con; adjust_constant_operand_info_for_microsoft_null_pointer_test( operand, &is_nullptr_constant, &null_con, (an_expr_node_ptr*)NULL); if (is_nullptr_constant) con->null_pointer_constant_ruled_out = FALSE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ make_constant_operand(con, operand); restore_operand_details(operand, &orig_operand); } else if (is_pointer_type(operand->type)) { a_constant_ptr conaddr = local_constant(); if (constant_prvalue_pointer(operand->variant.expression, conaddr, /*address_escapes=*/TRUE)) { orig_operand = *operand; if (curr_expr_kind_is_one_in_which_const_exprs_are_recorded()) { conaddr->expr = operand->variant.expression; } /* if */ make_constant_operand(conaddr, operand); restore_operand_details(operand, &orig_operand); operand->is_simple_string_literal = orig_operand.is_simple_string_literal; } /* if */ release_local_constant(&conaddr); } /* if */ release_local_constant(&con); } /* force_operand_to_constant_if_possible_full */ a_boolean expr_interpret_expression_operand(an_operand *operand, a_boolean must_be_constant, a_boolean is_constant_evaluated, /* Defaulted: */ a_boolean force_prvalue) /* Interpret the given expression operand. If successful, return TRUE and replace *operand by a corresponding constant operand. Otherwise return FALSE, and, if must_be_constant is TRUE, issue a diagnostic (if diagnostics should be issued) and make *operand an error operand. is_constant_evaluated indicates the result of calling std::is_constant_evaluated() during the interpretation of the given expression. force_prvalue (which is FALSE by default) is the force_prvalue flag value passed to the interpret_expr function: If TRUE, it forces converting the given operand to a prvalue (without updating the original expression tree). */ { a_boolean result; a_constant_ptr constant = local_constant(); a_diag_list diag_list; check_assertion(is_expression_operand(operand)); clear_diag_list(&diag_list); if (interpret_expr(operand->variant.expression, is_constant_evaluated, force_prvalue, constant, &diag_list)) { an_operand orig_operand; orig_operand = *operand; if (!curr_expr_kind_is_one_in_which_const_exprs_are_recorded()) { constant->expr = NULL; } /* if */ make_constant_operand(constant, operand); if (is_an_lvalue(&orig_operand) && is_any_reference_type(constant->type)) { /* make_constant_operand turns reference ck_address entries into prvalue operands. Restore glvalueness. */ operand->state = orig_operand.state; operand->type = orig_operand.type; } /* if */ restore_operand_details(operand, &orig_operand); result = TRUE; } else if (is_uninstantiated_dependent_expr_context() && operand_is_instantiation_dependent(operand)) { /* In template-dependent contexts, assume the expression might become constant after instantiation. */ make_template_param_expr_constant_operand(operand); result = TRUE; } else { result = FALSE; if (must_be_constant) { if (expr_error_should_be_issued()) { error_with_notes(ec_expr_not_constant, &operand->position, &diag_list); } /* if */ make_error_operand(operand); } /* if */ } /* if */ discard_more_info_list(&diag_list); release_local_constant(&constant); return result; } /* expr_interpret_expression_operand */ static inline a_boolean is_fs_node_reclaimable(an_expr_node_ptr node) /* Given an expression node, return TRUE if the node is in file scope and can be safely reclaimed; otherwise, return FALSE. */ { return in_file_scope(node); } /* is_fs_node_reclaimable */ static inline void reclaim_node_if_possible(an_expr_node_ptr node) /* If the given expression node is allocated in file-scope memory, place it on the avail_fs_nodes list. */ { if (is_fs_node_reclaimable(node)) { mark_fs_node_reclaimed(node); } /* if */ } /* reclaim_node_if_possible */ static inline void check_if_fs_node_reclaimable( an_expr_node_ptr node, an_expr_or_stmt_traversal_block_ptr tblock) /* If the given expression node is not a shallow copy and can itself be reclaimed, allow subtree processing to continue; otherwise, cull the subtree expression walk (as a non-reclaimable expression cannot have reclaimable sub-expressions). Note: This function is intended for traversal-based processing. See reclaim_fs_nodes_of_expr_tree for usage. See reclaim_node_if_possible for non-traversal-based cases. */ { if (!is_fs_node_reclaimable(node) || node->is_shallow_copy) { tblock->suppress_subtree_walk = TRUE; } /* if */ } /* check_if_fs_node_reclaimable */ static inline void reclaim_fs_node( an_expr_node_ptr node, ARG_UNUSED an_expr_or_stmt_traversal_block_ptr tblock) /* If the given expression node is allocated in file-scope memory, place it on the avail_fs_nodes list. Note: This function is intended for traversal-based processing. See reclaim_fs_nodes_of_expr_tree for usage. See reclaim_node_if_possible for non-traversal-based cases. */ { if (is_fs_node_reclaimable(node)) { mark_fs_node_reclaimed(node); } /* if */ } /* reclaim_fs_node */ static void reclaim_fs_nodes_of_expr_tree(an_expr_node *expr_tree) /* Traverse the given expression tree and reclaim every file-scope-memory node it contains for potential reuse later on. */ { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = check_if_fs_node_reclaimable; tblock.process_post_expr = reclaim_fs_node; traverse_expr(expr_tree, &tblock); } /* reclaim_fs_nodes_of_expr_tree */ void reclaim_fs_nodes_of_operand(an_operand *opnd) /* Reclaim file-scope-memory nodes referred to by the given operand. */ { switch (opnd->kind) { case ok_expression: if (opnd->variant.expression != NULL) { reclaim_fs_nodes_of_expr_tree(opnd->variant.expression); opnd->variant.expression = NULL; } /* if */ break; case ok_constant: if (opnd->variant.constant.expr != NULL) { reclaim_fs_nodes_of_expr_tree(opnd->variant.constant.expr); opnd->variant.constant.expr = NULL; } /* if */ break; default: break; } /* switch */ } /* reclaim_fs_nodes_of_operand */ a_boolean constant_conv_function_result(a_routine_ptr conv_func, an_operand *source_operand, a_type_ptr result_type, a_constant_ptr result_con) /* If calling the given conversion function on the given source operand produces a constant result, return TRUE and set *result_con to that result. Otherwise, return FALSE. */ { a_boolean is_constant = FALSE; a_type_ptr return_type, func_parent; an_expr_node_ptr call_node, rout_node, src_node, value_node, last_src_node; a_base_class_ptr conv_func_bcp; a_diag_list diag_list; a_memory_region_number region_to_switch_back_to; /* Create an expression tree representing the conversion call and interpret it. */ /* Allocate expression nodes in the file-scope memory region so they can be reclaimed at the end. */ switch_to_file_scope_region(®ion_to_switch_back_to); call_node = alloc_expr_node((an_expr_node_kind)enk_operation); rout_node = alloc_expr_node((an_expr_node_kind)enk_routine); src_node = alloc_expr_node((an_expr_node_kind)enk_constant); last_src_node = src_node; value_node = alloc_expr_node((an_expr_node_kind)enk_operation); switch_back_to_original_region(region_to_switch_back_to); if (is_constant_operand(source_operand)) { src_node->is_lvalue = FALSE; src_node->is_xvalue = FALSE; src_node->variant.constant.ptr = &source_operand->variant.constant; src_node->type = source_operand->type; } else if (is_expression_operand(source_operand)) { *src_node = *source_operand->variant.expression; src_node->next = NULL; } else { goto done; } /* if */ func_parent = parent_class_of(conv_func); conv_func_bcp = find_base_class_of(src_node->type, func_parent); if (conv_func_bcp != NULL) { if (is_glvalue_node(src_node)) { src_node = add_address_of_to_node(src_node); src_node = base_class_selection_expr(src_node, conv_func_bcp); src_node = add_indirection_to_node(src_node); } else { src_node = base_class_rvalue_expr(src_node, conv_func_bcp); } /* if */ } /* if */ rout_node->variant.routine.ptr = conv_func; rout_node->type = conv_func->type; rout_node->next = src_node; return_type = skip_typerefs(conv_func->type)->variant.routine.return_type; if (is_any_reference_type(return_type)) { set_node_operator(call_node, (an_expr_operator_kind)eok_dot_member_call, return_type, /*is_lvalue=*/TRUE, rout_node); set_node_operator(value_node, (an_expr_operator_kind)eok_ref_indirect, result_type, is_any_reference_type(result_type), call_node); } else { set_node_operator(call_node, (an_expr_operator_kind)eok_dot_member_call, return_type, /*is_lvalue=*/FALSE, rout_node); set_node_operator(value_node, (an_expr_operator_kind)eok_cast, result_type, /*is_lvalue=*/FALSE, call_node); } /* if */ clear_diag_list(&diag_list); is_constant = interpret_expr(value_node, /*is_constant_evaluated=*/TRUE, /*force_rvalue=*/FALSE, result_con, &diag_list); discard_more_info_list(&diag_list); done: reclaim_node_if_possible(call_node); reclaim_node_if_possible(rout_node); for (;;) { an_expr_node_ptr node = src_node; a_boolean is_last_node = TRUE; if (src_node != last_src_node && node_is_operator(src_node, eok_base_class_cast)) { src_node = src_node->variant.operation.operands; is_last_node = FALSE; } /* if */ reclaim_node_if_possible(node); if (is_last_node) break; } /* for */ reclaim_node_if_possible(value_node); return is_constant; } /* constant_conv_function_result */ a_boolean error_on_nonconstant_constant(ARG_UNUSED a_constant *constant, ARG_UNUSED a_source_position *pos) /* If constant is a constant entry that nevertheless does not have a constant value, issue an error at the indicated position and return TRUE. Otherwise, return FALSE. This is used for certain UPC constants that don't really have constant values. */ { a_boolean err = FALSE; #if UPC_EXTENSIONS_ALLOWED if (upc_mode) { /* We cannot use THREADS or MYTHREAD as a constant initializer. */ if (constant->kind == (a_constant_repr_kind)ck_upc_threads) { expr_pos_error(ec_threads_constant_not_allowed, pos); err = TRUE; } else if (constant->kind == (a_constant_repr_kind)ck_upc_mythread) { expr_pos_error(ec_mythread_constant_not_allowed, pos); err = TRUE; } /* if */ } /* if */ #endif /* UPC_EXTENSIONS_ALLOWED */ return err; } /* error_on_nonconstant_constant */ void make_template_param_constant_from_operand(an_operand *operand, a_constant_ptr result_con, a_type_ptr type) /* Turn the given operand into a ck_template_param constant (in *result_con). If type is non-NULL, first perform an implicit generic cast of the operand to that type. */ { an_expr_node_ptr node; if (type != NULL) { generic_cast_operand(operand, type, csf_none, /*is_implicit_cast=*/TRUE); } /* if */ node = make_node_from_operand(operand); make_template_param_expr_constant(node, result_con); if (expr_stack->possible_rescan_context) { /* Save rescan info if we may rescan this constant later. */ result_con->rescan_info = save_operand_info_in_rescan_info_entry( operand, (an_expr_rescan_info_entry_ptr)NULL); } /* if */ } /* make_template_param_constant_from_operand */ void extract_constant_from_operand(an_operand *operand, a_constant_ptr constant) /* Extract the constant value from the operand *operand and place it in *constant. */ { check_assertion(expr_stack != NULL); switch (operand->kind) { case ok_error: set_error_constant(constant); break; case ok_constant: copy_constant(&operand->variant.constant, constant); if (operand->name_reference_set && constant->kind != (a_constant_repr_kind)ck_template_param && curr_expr_kind_is_one_in_which_const_exprs_are_recorded()) { /* The operand has an associated name reference. Name references for dependent constants are redundant and ignored, but otherwise we need a constant with a backing expression referring to the primary constant in order to preserve the name reference. We use a shared constant for the target of the backing expression to facilitate reuse of allocated name references, as they are placed on a linked list rooted in the shared constant's source_corresp.name_references field. */ a_constant_ptr shared_constant = alloc_shareable_constant(&operand->variant.constant); an_expr_node_ptr expr = alloc_node_for_constant(shared_constant); expr->variant.constant.name_reference = find_allocated_name_reference( &shared_constant->source_corresp, &operand->name_reference); if (expr->variant.constant.name_reference != NULL) { constant->expr = expr; /* If this operand represents the definition of a named constant (enumerator or non-standard member constant), the shared constant will fill that role; the constant with the backing expression is a reference to the definition, not the definition itself, so clear the relevant flag in case it was set. */ constant->is_named_constant_definition = FALSE; } /* if */ } /* if */ break; case ok_expression: if (constexpr_enabled) { /* Attempt to interpret the expression. A failure will produce a diagnostic indicating the reason the operand is non-constant. */ a_diag_list diag_list; clear_diag_list(&diag_list); if (interpret_expr(operand->variant.expression, /*is_constant_evaluated=*/TRUE, /*force_prvalue=*/FALSE, constant, &diag_list)) { if (!curr_expr_kind_is_one_in_which_const_exprs_are_recorded()) { constant->expr = NULL; } /* if */ } else if (is_uninstantiated_dependent_expr_context() && operand_is_instantiation_dependent(operand)) { make_template_param_constant_from_operand(operand, constant, (a_type_ptr)NULL); } else { if (expr_error_should_be_issued()) { error_with_notes(ec_expr_not_constant, &operand->position, &diag_list); } /* if */ set_error_constant(constant); } /* if */ discard_more_info_list(&diag_list); break; } /* if */ FALLTHROUGH default: error_in_operand(ec_expr_not_constant, operand); set_error_constant(constant); break; } /* switch */ if (expr_stack->possible_rescan_context) { /* Save rescan info if we may rescan this constant later. */ constant->rescan_info = save_operand_info_in_rescan_info_entry( operand, (an_expr_rescan_info_entry_ptr)NULL); } /* if */ } /* extract_constant_from_operand */ void discard_operand(an_operand *operand) /* Discard the indicated operand. It has been scanned as a normal operand, but it's now known that it should have been a not-evaluated operand. This is only used in C++, for some strange cases. */ { /* The references in the operand aren't real references. */ change_ref_kinds(operand->ref_entries_list, SRK_REFERENCE); if (is_expression_operand(operand)) { /* If the operand contains destructible entities, remove the destructions from the object lifetime lists. */ unlink_expr_destructions(operand->variant.expression); } /* if */ } /* discard_operand */ a_boolean in_potential_constant_constexpr_context(void) /* Return TRUE if we're inside the definition of a C++11 constexpr function or constructor, including the header, and we're in an expression that is not required to be constant in the definition but might be constant in an invocation of the function, e.g., the return expression. Used to avoid setting the constant_expr_ruled_out flag for expressions that might turn out to be constant in the actual use. (Return false in C++14 mode, since folding of C++14 constexpr functions is done using the interpreter.) */ { a_boolean potential_constant_context = FALSE; if (constexpr_enabled) { if (innermost_function_scope != NULL && current_routine_entry()->is_constexpr && !curr_expr_kind_is_const()) { potential_constant_context = TRUE; } else if (scope_stack_top().in_field_initializer) { potential_constant_context = TRUE; } /* if */ } /* if */ return potential_constant_context; } /* in_potential_constant_constexpr_context */ a_boolean call_did_not_fold_to_constant(a_routine_ptr routine, an_operand *operand, a_boolean no_diagnostic, a_diag_list_ptr diag_list, a_source_position *pos) /* A call (or call-like construct) has been allowed with the hope that it would fold to a constant. It's now known that it has not, so issue an error on the given operand if appropriate (this is also called in non-constant expressions, so that for C++11 it can record something that rules out a constant expression). routine indicates the routine that was called, or is NULL if we don't know the specific routine (e.g., because of an error, or because the call was mapped to some other nonconstant construct). operand is NULL for a constructor invocation (in that case pos gives the source position to use) and is an operand representing the call otherwise. no_diagnostic is TRUE if the caller has determined that no diagnostic is justified even in a context that expects a constant expression (e.g., because we are possibly in a sub-expression that can turn out to produce a constant when the full expression is evaluated). If diag_list is non-NULL, it describes diagnostic notes with details of why folding failed. Return TRUE if an error was issued. */ { an_error_code err_code; a_boolean err = FALSE; if (routine == NULL || special_kind_is(routine, sfk_none) || special_kind_is(routine, sfk_udl_operator)) { /* Use a more specific error code for ordinary function calls and literal operator calls. */ err_code = constexpr_enabled ? ec_bad_cpp11_constant_function_call : ec_bad_constant_function_call; } else { err_code = ec_expr_not_constant; } /* if */ if (operand == NULL || !is_error_operand(operand)) { if (expr_stack != NULL && in_potential_constant_constexpr_context() && (routine == NULL || routine->is_constexpr || #if GNU_EXTENSIONS_ALLOWED (routine->implicit_alias && gpp_mode && !clang_mode) || #endif /* GNU_EXTENSIONS_ALLOWED */ ((clang_mode || gpp_mode || microsoft_mode) && routine->is_template_function && !routine->is_specialized) #if BUILTIN_FUNCTIONS_ENABLED || (builtin_functions_enabled && is_foldable_gnu_builtin_function(routine, (a_boolean*)NULL)) #endif /* BUILTIN_FUNCTIONS_ENABLED */ )) { /* This is a reference inside a constexpr function to a routine that is or might be constexpr. Do not set the flag indicating that a constant expression has been ruled out, because in an invocation of the constexpr function the call might be folded to a constant. If the current expression is a constant expression, go on to the tests below where we will issue an error. */ } else if (constexpr_enabled && (expr_stack == NULL || curr_expr_kind_is_evaluated_const()) && scope_is(&scope_stack_top(), sck_template_declaration)) { /* A dependent call in a nested template declaration scope might call a constexpr function and be folded, so turn it into a constant and await a real instantiation. */ if (operand != NULL) { if (scope_stack_top().is_rescan && !operand_is_instantiation_dependent(operand)) { /* In a SFINAE context is_template_dependent_context() is TRUE, but the rescan may be "real". In such situations, don't force the representation into a constant if folding failed. */ } else { make_template_param_expr_constant_operand(operand); } /* if */ } /* if */ } else if (expr_stack != NULL && curr_expr_kind_is_traditional_const()) { /* Unfolded routine calls are not allowed in constant expressions. */ err = TRUE; if (operand != NULL) { error_in_operand(err_code, operand); } else { expr_pos_error(err_code, pos); } /* if */ } else if (constexpr_enabled && !no_diagnostic) { /* The following is similar to a call to construct_not_allowed_in_cpp11_constant_expr, except that it appends the diag_list notes if necessary. */ if (expr_stack == NULL) { a_diagnostic_ptr dp; if (routine != NULL && special_kind_is(routine, sfk_constructor) && is_default_constructor(routine, /*is_declarative_context=*/TRUE)) { /* Use a more specific message for default constructor invocations. */ dp = pos_ty_start_error(ec_default_ctor_call_not_constant, operand != NULL ? &operand->position : pos, parent_class_of(routine)); } else { dp = pos_start_error(err_code, operand != NULL ? &operand->position : pos); } /* if */ if (diag_list != NULL) { add_more_info_list(dp, diag_list); } /* if */ end_diagnostic(dp); err = TRUE; } else if (curr_expr_is_evaluated() && !curr_expr_is_potentially_unevaluated() && (routine == NULL || !routine->is_constexpr)) { /* Constant expressions allow invalid operators/constructs in unevaluated subexpressions, including dead operands of "?", "&&", and "||". */ expr_stack->constant_expr_ruled_out = TRUE; } /* if */ } /* if */ } /* if */ return err; } /* call_did_not_fold_to_constant */ a_boolean consteval_failure(a_routine_ptr rp, a_constant_ptr result_con, a_source_position *pos, a_diag_list *diag_list) /* rp is a consteval function being called at the given position and folding the call failed (for reasons indicated by diag_list): If this is a context where constant-evaluation is mandatory, issue a diagnostic, set *result_con to an error constant (if result_con is non_NULL), and return TRUE. Otherwise, return FALSE. For the case of a call that is the argument of another call, we cannot tell at this time whether it is valid or not (until we know whether the enclosing call is to a consteval function). In such cases, record a pending diagnostic if appropriate. */ { a_boolean result; if (!(expr_stack != NULL && (expr_stack->is_default_arg_expression || expr_stack->consteval_call_need_not_fold)) && !scope_stack_top().in_field_initializer && !consteval_escalation()) { if (expr_stack != NULL && expr_stack->in_call_argument) { /* Record a pending consteval failure, unless there already is one. */ if (pending_consteval_failure.routine == NULL) { pending_consteval_failure.routine = rp; pending_consteval_failure.diag_pos = *pos; pending_consteval_failure.diag_list = *diag_list; clear_diag_list(diag_list); } else { discard_more_info_list(diag_list); } /* if */ result = FALSE; } else { /* This is a context where consteval calls must produce a constant value: Issue an error if appropriate. */ if (rp->is_deleted) { /* An error should have been issued already. An additional one is not really helpful. */ expect_error(); } else if (expr_stack != NULL && expr_stack->suppress_diagnostics) { record_suppressed_error(); } else { a_diagnostic_ptr dp; dp = pos_sy_start_error(ec_consteval_call_nonconstant, pos, symbol_for(rp)); add_more_info_list(dp, diag_list); end_diagnostic(dp); } /* if */ if (result_con != NULL) { set_error_constant(result_con); } /* if */ result = TRUE; } /* if */ } else { result = FALSE; } /* if */ if (!result) { /* Although the call need not be evaluated at this time, we are still going to record it in the IL. Ensure that it is marked as needed. */ #if MAINTAIN_NEEDED_FLAGS mark_as_needed((char*)rp, (an_il_entry_kind)iek_routine); #endif /* MAINTAIN_NEEDED_FLAGS */ } /* if */ return result; } /* consteval_failure */ static void eliminate_stmt_expr_refs_if_backing_expr_dropped( an_expr_node_ptr expr, a_constant_ptr con) /* expr is an expression that was folded to con. If con dropped the backing expression (con->expr == NULL), eliminate any IL entries that refer to statements in GNU statement expressions in expr. (Currently, this includes only source sequence entries and pragmas.) */ { if (expr != NULL && con->expr == NULL && (con->folded_statement_expression || expr_stack->statement_expression_seen)) { eliminate_statement_expr_pragmas(expr); #if GENERATE_SOURCE_SEQUENCE_LISTS eliminate_statement_expr_src_seq_entries(expr); #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ } /* if */ } /* eliminate_stmt_expr_refs_if_backing_expr_dropped */ static void eliminate_stmt_expr_refs_if_constant_dropped(a_constant_ptr con) /* con is a constant operand whose expression is being folded into a larger constant without preserving the operand's backing expression. If that backing expression came from a folded GNU statement expression, eliminate any IL entries that refer to statements in that dropped expression. */ { if (con != NULL && con->expr != NULL && con->folded_statement_expression) { eliminate_statement_expr_pragmas(con->expr); #if GENERATE_SOURCE_SEQUENCE_LISTS eliminate_statement_expr_src_seq_entries(con->expr); #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ } /* if */ } /* eliminate_stmt_expr_refs_if_constant_dropped */ static void eliminate_stmt_expr_refs_if_operand_dropped(an_operand *operand) /* operand is being discarded without preserving its expression tree. If that tree contains GNU statement expressions, eliminate any IL entries that refer to statements in the dropped expression. */ { if (is_expression_operand(operand)) { if (expr_stack->statement_expression_seen) { eliminate_statement_expr_pragmas(operand->variant.expression); #if GENERATE_SOURCE_SEQUENCE_LISTS eliminate_statement_expr_src_seq_entries(operand->variant.expression); #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ } /* if */ } else if (is_constant_operand(operand)) { eliminate_stmt_expr_refs_if_constant_dropped(&operand->variant.constant); } /* if */ } /* eliminate_stmt_expr_refs_if_operand_dropped */ static a_boolean expr_fold_constexpr_call(an_expr_node_ptr call_expr, a_routine_ptr *p_rout, an_operand *result, a_diag_list_ptr diag_list) /* Interface to interpret_constexpr_call for use within the expression-processing routines. This function attempts to fold the call call_expr to a constant; if it can, return TRUE and set *result to an operand for the result. Otherwise, return FALSE and leave *result unchanged (diagnostic nodes are potentially added to *diag_list). For a direct call, *p_rout indicates the called routine; for indirect calls, *p_rout is NULL but this function sets it to the called function if possible (i.e., if evaluation can proceed far enough to determine the call target). */ { a_boolean folded = FALSE, is_consteval; a_routine_ptr rout = *p_rout; if (rout == NULL) { if (!is_template_dependent_type(call_expr->type)) { rout = get_constexpr_callee(call_expr, diag_list); } /* if */ if (rout == NULL) goto done; *p_rout = rout; } /* if */ if (rout->is_consteval) { is_consteval = TRUE; call_expr->variant.operation.is_consteval_call = TRUE; } else { is_consteval = FALSE; } /* if */ if (((constexpr_call_folding_should_be_done() && !expr_stack->template_deduction_context) || is_consteval) && (!expr_stack->in_noexcept_operand_expression || core_constant_expr_is_noexcept)) { /* Do not force folding a call during SFINAE processing unless this is a consteval function, because doing so could trigger the instantiation of the callee with potential errors outside the immediate context. If a constant value is really needed, interpretation will be forced at a higher level. */ a_constant_ptr result_con = local_constant(); a_boolean release_constant = TRUE; folded = interpret_constexpr_call(call_expr, is_consteval, result_con, diag_list); if (folded) { eliminate_stmt_expr_refs_if_backing_expr_dropped(call_expr, result_con); make_constant_operand(result_con, result); result->position = call_expr->position; if (is_reference_type(result->type)) { a_boolean is_rvalue_ref = is_rvalue_reference_type(result->type); add_reference_indirection(result); if (is_rvalue_ref) { /* A call of a function that returns an rvalue reference is an xvalue. */ conv_rvalue_reference_result_to_xvalue(result); } /* if */ } else if (!curr_expr_kind_is_const() && is_class_struct_union_type(result->type)) { /* Create a temporary to hold the result of the function call. */ a_dynamic_init_ptr temp_dip; an_expr_node_ptr temp_node; temp_node = create_expr_temporary(result->type, /*is_lvalue=*/FALSE, /*is_explicit_cast=*/FALSE, /*suppress_abstract_test=*/TRUE, (a_dynamic_init_kind)dik_constant, &call_expr->position, &temp_dip); set_dynamic_init_constant(temp_dip, move_local_constant_to_il(&result_con)); release_constant = FALSE; make_expression_operand(temp_node, result); } /* if */ } else if (is_consteval) { if (expr_is_instantiation_dependent(call_expr)) { make_expression_operand(call_expr, result); make_template_param_expr_constant_operand(result); } else if (expr_stack != NULL && is_any_reattempt_permitted( expr_stack->const_eval_reattempt_state)) { /* Wrap the source location expression in a node signifying its evaluation should be deferred. */ an_expr_node_ptr deferral_expr = alloc_expr_node(enk_const_eval_deferred); deferral_expr->type = call_expr->type; deferral_expr->position = call_expr->position; auto &variant = deferral_expr->variant.const_eval_deferred; variant.wrapped = call_expr; variant.reattempt_state = expr_stack->const_eval_reattempt_state; make_expression_operand(deferral_expr, result); } else if (consteval_failure(rout, result_con, &call_expr->position, diag_list)) { /* The folding of a consteval call failed in a context where it cannot fail: Proceed as if folding succeeded. */ make_constant_operand(result_con, result); folded = TRUE; } /* if */ } /* if */ if (release_constant) release_local_constant(&result_con); /* Check for calls to std::is_constant_evaluated() and __builtin_is_constant_evaluated(), and warn if they appear in a function that is not plain "constexpr". */ if ((rout->is_constexpr_intrinsic && rout->number.constexpr_intrinsic == (int32_t)cit_std_is_constant_evaluated) || rout_is_specific_builtin(rout, bufk_is_constant_evaluated)) { an_error_code err_code = ec_no_error; if (!scope_stack_top().is_rescan && innermost_function_scope != NULL) { a_routine_ptr curr_rp = current_routine_entry(); if (curr_rp->is_consteval && !curr_rp->is_prototype_instantiation) { err_code = ec_is_constant_evaluated_in_consteval_context; } else if (!curr_rp->is_constexpr && !curr_expr_kind_is_const() && struct_stmt_stack_top().kind != (a_struct_stmt_kind)ssk_constexpr_if && !expr_stack->is_vla_dimension_expression) { /* Don't warn here if we are in a constant-expression context. Note that constexpr-if conditions are not parsed as constant- expressions (they're pre-scanned and evaluated later), but this warning is not correct for them. */ err_code = ec_is_constant_evaluated_in_nonconstexpr_context; } /* if */ } /* if */ if (err_code != ec_no_error) { pos_st_warning(err_code, &call_expr->position, unmangled_name_of(&rout->source_corresp)); } /* if */ } /* if */ } /* if */ done: return folded; } /* expr_fold_constexpr_call */ static a_boolean expr_fold_constexpr_ctor( a_routine_ptr ctor, a_dynamic_init_ptr ctor_dip, a_source_position *pos, a_boolean check_constexpr, a_constant *result_con) /* Interface to fold_constexpr_ctor for use within the expression-processing routines. ctor is the constructor being called. See fold_constexpr_ctor for the description of the remaining parameters. */ { a_boolean folded = FALSE; if ((constexpr_call_folding_should_be_done() && /* Exclude constant-expressions, because those will be handled when the expression is complete. */ (!curr_expr_kind_is_const() || check_constexpr)) || (ctor->is_consteval && !is_prototype_instantiation_or_expansion_context())) { a_boolean need_backing_expr = curr_expr_kind_is_one_in_which_const_exprs_are_recorded(); if (fold_constexpr_ctor(ctor_dip, need_backing_expr, check_constexpr, ctor->is_consteval, pos, result_con)) { folded = TRUE; } /* if */ } /* if */ return folded; } /* expr_fold_constexpr_ctor */ static a_boolean operand_allows_is_operand_of_address_of(an_operand *operand) /* Return TRUE if the given operand is one on which the is_operand_of_address_of flag is allowed to be set. Such cases are indefinite functions in various forms. */ { a_boolean allowed = FALSE; if (is_indefinite_function_operand(operand)) { allowed = TRUE; } else if (is_constant_operand(operand)) { a_constant_ptr con = &operand->variant.constant; if (con->kind == ck_template_param) { if (tpck_is(con, tpck_template_ref)) { con = con->variant.template_param.variant.template_ref.con; } /* if */ if (tpck_is(con, tpck_unknown_function)) { allowed = TRUE; } /* if */ } /* if */ } /* if */ return allowed; } /* operand_allows_is_operand_of_address_of */ static void restore_operand_expr_position_if_expr(an_operand *operand, an_operand *orig_operand) /* This function is used to apply position information to a new operand expression while considering information from the original operand state. The position information is prioritized as follows: 1. The current position information (if present) 2. The original operand's expression's position information (if present) 3. The new operator's position information */ { an_expr_node *expr = maybe_get_operand_expr_with_pos(operand); if (expr != NULL) { an_expr_node *orig_expr = maybe_get_operand_expr_with_pos(orig_operand); a_source_position *op_pos; a_source_position *start_pos; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position *end_pos; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* Use the original expression's position information if present. */ if (expr->position.seq != 0) { op_pos = &expr->position; } else { if (orig_expr != NULL && orig_expr->position.seq != 0) { op_pos = &orig_expr->position; } else { op_pos = &operand->position; } /* if */ } /* if */ #if EXTRA_SOURCE_POSITIONS_IN_IL if (expr->expr_range.start.seq != 0) { start_pos = &expr->expr_range.start; } else { if (orig_expr != NULL && orig_expr->expr_range.start.seq != 0) { start_pos = &orig_expr->expr_range.start; } else { start_pos = &operand->position; } /* if */ } /* if */ if (expr->expr_range.end.seq != 0) { end_pos = &expr->expr_range.end; } else { if (orig_expr != NULL && orig_expr->expr_range.end.seq != 0) { end_pos = &orig_expr->expr_range.end; } else { end_pos = &operand->end_position; } /* if */ } /* if */ #else /* !EXTRA_SOURCE_POSITIONS_IN_IL */ start_pos = &operand->position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ set_expr_position(expr, start_pos, end_pos, op_pos); } /* if */ } /* restore_operand_expr_position_if_expr */ void restore_operand_details(an_operand *operand, an_operand *orig_operand) /* *operand has been subjected to some sort of modification, which may have destroyed its source position, etc. Restore such things from *orig_operand, which is a copy of *operand before the modification. */ { /* This routine must be callable even when there is nothing on the expression stack. */ copy_operand_position(orig_operand, operand); /* If necessary, set the position in the expression too. */ if (is_expression_operand(orig_operand) && is_expression_operand(operand) && orig_operand->variant.expression == operand->variant.expression) { /* The expression is the same, so don't update it. This is important for cases where we've deliberately set a different position on the operand and the expression (e.g., because of indirection) and we'd like to preserve that over some transformation like lvalue-to-rvalue conversion. */ } else if (is_constant_operand(orig_operand) && is_constant_operand(operand) && orig_operand->variant.constant.expr != NULL && orig_operand->variant.constant.expr == operand->variant.constant.expr) { /* Same for a case where an expression was recorded for a constant. */ } else { restore_operand_expr_position_if_expr(operand, orig_operand); } /* if */ #if OPTIMIZE_VIRTUAL_FUNCTION_CALLS operand->orig_routine_type = orig_operand->orig_routine_type; #endif /* OPTIMIZE_VIRTUAL_FUNCTION_CALLS */ operand->bound_function = orig_operand->bound_function; operand->selector_is_object_pointer = orig_operand->selector_is_object_pointer; operand->virtual_function = orig_operand->virtual_function; operand->is_qualified_name = orig_operand->is_qualified_name; operand->access_control_error_reported = orig_operand->access_control_error_reported; operand->is_operand_of_address_of = (orig_operand->is_operand_of_address_of && operand_allows_is_operand_of_address_of(operand)); operand->has_required_ptr_to_member_form = orig_operand->has_required_ptr_to_member_form; operand->ruled_out_expr_kinds |= orig_operand->ruled_out_expr_kinds; operand->is_parenthesized = orig_operand->is_parenthesized; if (operand->is_operand_of_address_of) { operand->ampersand_position = orig_operand->ampersand_position; } /* if */ operand->caused_template_instantiation = orig_operand->caused_template_instantiation; operand->pack_expansion_descr = orig_operand->pack_expansion_descr; } /* restore_operand_details */ void restore_operand_details_incl_ref(an_operand *operand, an_operand *orig_operand) /* *operand has been subjected to some sort of modification, which may have destroyed its source position, etc. Restore such things from *orig_operand, which is a copy of *operand before the modification. Restore the ref_entries_list too (not usually wanted). */ { restore_operand_details(operand, orig_operand); operand->ref_entries_list = orig_operand->ref_entries_list; operand->saved_ref_entries_list = orig_operand->saved_ref_entries_list; } /* restore_operand_details_incl_ref */ void restore_operand_details_for_cast(an_operand *operand, an_operand *orig_operand, a_boolean is_implicit_cast, a_boolean incl_ref) /* Version of restore_operand_details customized for the result of cast operations. is_implicit_cast indicates whether the cast is implicit. incl_ref, if TRUE, requests restoration of the ref_entries_list also. */ { if (incl_ref) { restore_operand_details_incl_ref(operand, orig_operand); } else { restore_operand_details(operand, orig_operand); } /* if */ if (operand->pack_expansion_descr != NULL && !is_implicit_cast) { /* For explicit casts, make sure the pack expansion indication is cleared, because it was picked up on the operand. */ /* If some modes and configurations, an explicit cast can be elided. If that has happened in this case, keep the pack expansion information in the operand because it hasn't been recorded on the operand yet. */ an_expr_node_ptr expr = expr_node_from_operand(operand); if (expr == NULL || expr != expr_node_from_operand(orig_operand)) { operand->pack_expansion_descr = NULL; } /* if */ } /* if */ } /* restore_operand_details_for_cast */ void restore_operand_id_details(an_operand *operand, an_operand *orig_operand) /* Adjunct to restore_operand_details: restore those operand flags that have to do with the form of an identifier reference. They are not restored by default because a transformation on an operand might render it no longer an id-expression. */ { operand->is_id_expression = orig_operand->is_id_expression; operand->is_address_of_id_expression = orig_operand->is_address_of_id_expression; operand->id_expression_was_parenthesized = orig_operand->id_expression_was_parenthesized; operand->is_parenthesized = orig_operand->is_parenthesized; operand->is_name_followed_by_left_paren = orig_operand->is_name_followed_by_left_paren; } /* restore_operand_id_details */ void restore_operand_form_of_name_reference(an_operand *operand, an_operand *orig_operand) /* *operand has been subjected to some sort of modification, which may have destroyed its form-of-name-reference information. Restore that information (if any) from *orig_operand, which is a copy of *operand before the modification. */ { if (orig_operand->name_reference_set) { operand->name_reference_set = TRUE; operand->name_reference = orig_operand->name_reference; } /* if */ } /* restore_operand_form_of_name_reference */ static void preserve_ruled_out_expr_kinds_from_discarded_operand( an_operand *discarded_operand, an_operand *result_operand) /* The operand discarded_operand is being discarded in an operation whose result (result_operand) can be determined without evaluating that operand. Transfer any appropriate information about expression kinds ruled out by operations in the discarded operand, so the result properly reflects those. */ { result_operand->ruled_out_expr_kinds |= discarded_operand->ruled_out_expr_kinds; if (is_constant_operand(result_operand) && (!is_constant_operand(discarded_operand) || discarded_operand->variant.constant.null_pointer_constant_ruled_out)) { /* The result is not a null pointer constant. */ result_operand->variant.constant.null_pointer_constant_ruled_out = TRUE; } /* if */ } /* preserve_ruled_out_expr_kinds_from_discarded_operand */ void record_suppressed_error(void) /* Record that an error has been detected in a context in which we're suppressing errors. */ { check_assertion(expr_stack != NULL && expr_stack->suppress_diagnostics); expr_stack->any_suppressed_error = TRUE; } /* record_suppressed_error */ a_boolean expr_diagnostic_should_be_issued(an_error_severity sev, an_error_code err_code, a_source_position *pos) /* Return TRUE if a diagnostic with the indicated severity, error code, and position should be issued. More precisely, return FALSE if the diagnostic should not be issued because we are in a template deduction context. In that case, if the effective level of the diagnostic (after possible adjustment because of command-line options or pragmas) is an error of some kind, set the any_suppressed_error flag in the expression stack. */ { a_boolean should_issue = TRUE; if (expr_stack != NULL) { if (expr_stack->suppress_diagnostics) { should_issue = FALSE; if (is_effective_sfinae_error(err_code, sev, pos)) { record_suppressed_error(); } /* if */ } /* if */ } /* if */ return should_issue; } /* expr_diagnostic_should_be_issued */ a_boolean expr_error_should_be_issued(void) /* Return TRUE unless errors are suppressed because we're in SFINAE rescanning. Should not be called for access errors. */ { a_boolean should_issue = TRUE; if (expr_stack != NULL) { if (expr_stack->suppress_diagnostics) { should_issue = FALSE; /* Because an error cannot be downgraded, there's no issue of checking whether this is indeed an error. */ record_suppressed_error(); } /* if */ } /* if */ return should_issue; } /* expr_error_should_be_issued */ a_boolean is_consteval_diag_deferred() /* If folding of consteval function calls is not required in the current context (because folding should be reattempted later), return TRUE; otherwise, return FALSE. */ { a_boolean is_deferred = FALSE; if (expr_stack != NULL) { if (is_any_reattempt_permitted(expr_stack->const_eval_reattempt_state)) { is_deferred = TRUE; } /* if */ } /* if */ return is_deferred; } /* is_consteval_diag_deferred */ void expr_pos_st_warning(an_error_code error_code, a_source_position *error_pos, a_const_char *str) /* Report the indicated warning at the indicated position. Suppress the warning if we're in a context where diagnostics should be suppressed, e.g., a template deduction context. */ { if (expr_diagnostic_should_be_issued(es_warning, error_code, error_pos)) { pos_st_warning(error_code, error_pos, str); } /* if */ } /* expr_pos_st_warning */ void expr_pos_diagnostic(an_error_severity sev, an_error_code error_code, a_source_position *error_pos) /* Report the indicated diagnostic with the indicated severity at the indicated position. Suppress the diagnostic if we're in a context where diagnostics should be suppressed, e.g., a template deduction context. */ { if (expr_diagnostic_should_be_issued(sev, error_code, error_pos)) { pos_diagnostic(sev, error_code, error_pos); } /* if */ } /* expr_pos_diagnostic */ void expr_pos_sy_diagnostic(an_error_severity sev, an_error_code error_code, a_source_position *error_pos, a_symbol_ptr sym) /* Report the indicated diagnostic with the indicated severity at the indicated position. sym is used for a fill-in. Suppress the diagnostic if we're in a context where diagnostics should be suppressed, e.g., a template deduction context. */ { if (expr_diagnostic_should_be_issued(sev, error_code, error_pos)) { pos_sy_diagnostic(sev, error_code, error_pos, sym); } /* if */ } /* expr_pos_sy_diagnostic */ void expr_pos_ty_diagnostic(an_error_severity sev, an_error_code error_code, a_source_position *error_pos, a_type_ptr tp) /* Report the indicated diagnostic with the indicated severity at the indicated position. Suppress the diagnostic if we're in a context where diagnostics should be suppressed, e.g., a template deduction context. */ { if (expr_diagnostic_should_be_issued(sev, error_code, error_pos)) { pos_ty_diagnostic(sev, error_code, error_pos, tp); } /* if */ } /* expr_pos_ty_diagnostic */ void expr_pos_ty2_diagnostic(an_error_severity sev, an_error_code error_code, a_source_position *error_pos, a_type_ptr tp1, a_type_ptr tp2) /* Report the indicated diagnostic with the indicated severity at the indicated position. Suppress the diagnostic if we're in a context where diagnostics should be suppressed, e.g., a template deduction context. */ { if (expr_diagnostic_should_be_issued(sev, error_code, error_pos)) { pos_ty2_diagnostic(sev, error_code, error_pos, tp1, tp2); } /* if */ } /* expr_pos_ty2_diagnostic */ void expr_pos_st_diagnostic(an_error_severity sev, an_error_code error_code, a_source_position *error_pos, a_const_char *str) /* Report the indicated diagnostic with the indicated severity at the indicated position with the given string substitution. Suppress the diagnostic if we're in a context where diagnostics should be suppressed, e.g., a template deduction context. */ { if (expr_diagnostic_should_be_issued(sev, error_code, error_pos)) { pos_st_diagnostic(sev, error_code, error_pos, str); } /* if */ } /* expr_pos_st_diagnostic */ void expr_pos_st_error(an_error_code error_code, a_source_position *error_pos, a_const_char *str) /* Report the indicated error at the indicated position with the given string substitution. Suppress the error if we're in a context where diagnostics should be suppressed, e.g., a template deduction context. */ { if (expr_error_should_be_issued()) { pos_st_error(error_code, error_pos, str); } /* if */ } /* expr_pos_st_error */ void expr_pos_ty_error(an_error_code error_code, a_source_position *error_pos, a_type_ptr tp) /* Report the indicated error at the indicated position with the given type substitution. Suppress the error if we're in a context where diagnostics should be suppressed, e.g., a template deduction context. */ { if (expr_error_should_be_issued()) { pos_ty_error(error_code, error_pos, tp); } /* if */ } /* expr_pos_ty_error */ void expr_pos_ty2_error(an_error_code error_code, a_source_position *error_pos, a_type_ptr tp1, a_type_ptr tp2) /* Report the indicated error at the indicated position with the given type substitutions. Suppress the error if we're in a context where diagnostics should be suppressed, e.g., a template deduction context. */ { if (expr_error_should_be_issued()) { pos_ty2_error(error_code, error_pos, tp1, tp2); } /* if */ } /* expr_pos_ty2_error */ void expr_syntax_error(an_error_code error_code) /* Report the indicated error at the position indicated by error_position, then get and throw away tokens until a token is read that is in the set of stop tokens. This routine is called to report and recover from syntax errors in the expression routines. */ { /* Report the error. */ expr_pos_error(error_code, &error_position); /* Flush tokens until something in the stop token set turns up. */ flush_tokens(); } /* expr_syntax_error */ void expr_issue_incomplete_type_diag(a_source_position *pos, a_type *type, /* Defaulted: */ an_error_severity severity) /* Emit a diagnostic given the position where the incomplete type is used, the type that's incomplete, and the severity of the diagnostic. Suppress the diagnostic if we're in a context where diagnostics should be suppressed, e.g., a template deduction context. */ { an_error_code error_code = incomplete_type_error_code(type); if (expr_diagnostic_should_be_issued(severity, error_code, pos)) { issue_incomplete_type_diag(error_code, pos, type, severity); } /* if */ } /* expr_issue_incomplete_type_diag */ void expr_issue_sizeless_type_error(a_source_position *pos, a_type *type) /* Emit an error diagnostic given the position where the sizeless type is used and the type that's sizeless. Suppress the diagnostic if we're in a context where diagnostics should be suppressed, e.g., a template deduction context. */ { if (expr_error_should_be_issued()) { pos_ty_error(ec_sizeless_type_not_allowed, pos, type); } /* if */ } /* expr_issue_sizeless_type_error */ void expr_expect_error(void) /* Interface to expect_error for use in the expression routines. Does not set up the expectation of an error if we're in a context where errors are suppressed (e.g., a SFINAE context). */ { if (expr_error_should_be_issued()) expect_error(); } /* expr_expect_error */ a_boolean expr_access_checking_should_be_done(void) /* Return TRUE if access checking should be done in the current expression context. This used to suppress access-checking code in certain template- dependent contexts. In such contexts, the behavior should be as if the access checking is not done; it's not quite enough to simply suppress diagnostics, if that changes the control flow. */ { a_boolean check_access = TRUE; if (scope_stack[depth_scope_stack].in_prototype_instantiation) { /* No access checking in prototype instantiations, since we may not have complete information. */ check_access = FALSE; } else if (expr_stack != NULL) { if (expr_stack->template_deduction_context) { /* Depending on configuration and command-line options, access errors may or may not cause deduction failure. In Microsoft mode, an additional wrinkle applies: Even though Microsoft (as of version 19.00) does not treat access errors as SFINAE errors in general, it does effectively do so for access errors in decltype constructs appearing in return types. */ if (!cpp11_sfinae_enabled || (cpp11_sfinae_ignore_access && !(microsoft_mode && scope_stack_top().in_decltype_context && expr_stack->rcblock != NULL && (expr_stack->rcblock->options & CTWS_RETURN_TYPE) != 0))) { check_access = FALSE; } /* if */ } /* if */ } /* if */ return check_access; } /* expr_access_checking_should_be_done */ static a_boolean base_class_cast_access_checking_should_be_done(void) /* Return TRUE if access checking for a base class cast should be done. That's usually the same answer as expr_access_checking_should_be_done, but g++ does seem to check base class cast access in SFINAE contexts even though it does not do access checking in general in those contexts. */ { a_boolean check_access = expr_access_checking_should_be_done(); if (gpp_mode && expr_stack != NULL && expr_stack->template_deduction_context) { /* g++ (checked in 4.7) seems to check this access even though it does not in general check access during SFINAE. */ check_access = TRUE; } /* if */ return check_access; } /* base_class_cast_access_checking_should_be_done */ void make_error_operand(an_operand *operand) /* Create an error operand. */ { clear_operand((an_operand_kind)ok_error, operand); operand->type = error_type(); copy_source_position(error_position, operand->position); } /* make_error_operand */ void expr_check_ambiguity_and_verify_access(a_symbol_locator *locator) /* Wrapper for check_ambiguity_and_verify_access for use within the expression routines. */ { if (!C_mode()) { /* See if we're suppressing access checking, e.g., because we're in a prototype instantiation or because a member is accessed through splicing. */ if (expr_access_checking_should_be_done() && !locator->is_splicer) { a_boolean error_detected = FALSE; a_boolean *p_error_detected = NULL; /* If errors are suppressed, get a returned variable instead of issuing any error. */ if (expr_stack->suppress_diagnostics) p_error_detected = &error_detected; check_ambiguity_and_access_full(locator, /*is_template_context=*/FALSE, /*is_qualifier=*/FALSE, p_error_detected); if (error_detected) record_suppressed_error(); } else { /* Access checking is suppressed, so do only the ambiguity check. */ if (f_check_for_ambiguity(locator, /*is_template_context=*/FALSE, /*is_qualifier=*/FALSE, !expr_stack->suppress_diagnostics) && expr_stack->suppress_diagnostics) { record_suppressed_error(); } /* if */ } /* if */ } /* if */ } /* expr_check_ambiguity_and_verify_access */ void expr_overload_check_ambiguity_and_verify_access( a_symbol_locator *locator, a_symbol_ptr overloaded_symbol) /* Wrapper for overload_check_ambiguity_and_verify_access for use within the expression routines. */ { /* See if we're suppressing access checking, e.g., because we're in a prototype instantiation. */ if (expr_access_checking_should_be_done()) { a_boolean error_detected = FALSE; a_boolean *p_error_detected = NULL; /* If errors are suppressed, get a returned variable instead of issuing any error. */ if (expr_stack->suppress_diagnostics) p_error_detected = &error_detected; overload_check_ambiguity_and_verify_access(locator, overloaded_symbol, p_error_detected); if (error_detected) record_suppressed_error(); } else { /* Access checking is suppressed, so do only the ambiguity check to change the locator to an error locator if an error would have been issued. */ if (f_check_for_ambiguity(locator, /*is_template_context=*/FALSE, /*is_qualifier=*/FALSE, /*diagnostic_should_be_issued=*/FALSE)) { set_to_error_locator(*locator); } /* if */ } /* if */ } /* expr_overload_check_ambiguity_and_verify_access */ void operand_will_not_be_used_because_of_error(an_operand *operand) /* The indicated operand will not be used further because an error has been detected. There is also the implication that because of the error we cannot tell how the operand would have been used. Do any cleanup required. */ { /* Change the references to errors. */ change_operand_refs_to_error(operand); /* Free any dynamically-allocated attachments to the operand. */ free_attachments_to_operand(operand); } /* operand_will_not_be_used_because_of_error */ static void arg_list_elem_will_not_be_used_because_of_error( an_arg_list_elem_ptr alep) /* The indicated arg-list element will not be used further because an error has been detected. There is also the implication that because of the error we cannot tell how the operands would have been used. Do any cleanup required. The entry is not freed. */ { if (is_expression_component(alep)) { operand_will_not_be_used_because_of_error(operand_of_arg_list_elem(alep)); } else if (is_braced_init_component(alep)) { arg_list_will_not_be_used_because_of_error(alep->variant.braced.list); } else if (is_designator_component(alep)) { /* No action required. */ } else { unexpected_condition(); } /* if */ } /* arg_list_elem_will_not_be_used_because_of_error */ void arg_list_will_not_be_used_because_of_error( an_arg_list_elem_ptr operand_list) /* The indicated expression list will not be used further because an error has been detected. There is also the implication that because of the error we cannot tell how the operands would have been used. Do any cleanup required. The list is not freed. */ { an_arg_list_elem_ptr alep; for (alep = operand_list; alep != NULL; alep = next_elem(alep)) { arg_list_elem_will_not_be_used_because_of_error(alep); } /* for */ } /* arg_list_will_not_be_used_because_of_error */ static void change_to_error_operand(an_operand *operand) /* Take an existing operand and convert it to an error operand. Retain some information, like source position. This routine can be used to "neuter" an operand, in particular to detach it from its subtree without freeing the subtree; there is no implication that there was an error. See conv_to_error_operand for the usual case. */ { set_operand_kind(operand, (an_operand_kind)ok_error); operand->type = error_type(); operand->state = (an_operand_state)os_none; operand->is_simple_string_literal = FALSE; operand->is_id_expression = FALSE; operand->is_address_of_id_expression = FALSE; operand->id_expression_was_parenthesized = FALSE; /* bound_function is not cleared on purpose. */ operand->selector_is_object_pointer = FALSE; operand->is_operand_of_address_of = FALSE; operand->has_required_ptr_to_member_form = FALSE; } /* change_to_error_operand */ void conv_to_error_operand(an_operand *operand) /* Take an existing operand and convert it to an error operand, to reflect that there was an error on the operand. Retain some information, like source position. */ { operand_will_not_be_used_because_of_error(operand); change_to_error_operand(operand); } /* conv_to_error_operand */ void normalize_error_operand(an_operand *operand) /* Take an operand for which is_error_operand returns TRUE, and normalize it. For operands that have an error type but not an ok_error kind, that means converting them to full error operands. */ { if (operand->kind != (an_operand_kind)ok_error) { conv_to_error_operand(operand); } /* if */ } /* normalize_error_operand */ void error_and_make_error_operand(an_error_code error_code, an_operand *operand) /* Announce an error and set the operand to an error operand. */ { expr_pos_error(error_code, &error_position); make_error_operand(operand); } /* error_and_make_error_operand */ void error_in_operand(an_error_code error_code, an_operand *operand) /* Announce an error at the position in the operand and convert the operand to an error operand. */ { expr_pos_error(error_code, &operand->position); conv_to_error_operand(operand); } /* error_in_operand */ static void sym_error_in_operand(an_error_code error_code, an_operand *operand, a_symbol_ptr sym) /* Announce an error at the position in the operand and convert the operand to an error operand. The symbol sym is cited in the error message. */ { if (expr_error_should_be_issued()) { pos_sy_error(error_code, &operand->position, sym); } /* if */ conv_to_error_operand(operand); } /* sym_error_in_operand */ void type_error_in_operand(an_error_code error_code, an_operand *operand, a_type_ptr type) /* Announce an error at the position in the operand and convert the operand to an error operand. The given type is cited in the error message. */ { expr_pos_ty_error(error_code, &operand->position, type); conv_to_error_operand(operand); } /* type_error_in_operand */ void type2_error_in_operand(an_error_code error_code, an_operand *operand, a_type_ptr type1, a_type_ptr type2) /* Announce an error at the position in the operand and convert the operand to an error operand. The two types are cited in the error message. */ { expr_pos_ty2_error(error_code, &operand->position, type1, type2); conv_to_error_operand(operand); } /* type2_error_in_operand */ void expr_clear_init_state(an_init_state *init_state) /* Version of clear_init_state to be used within the expression routines. Fills in some things from expression context. */ { clear_init_state(init_state); if (curr_expr_kind_is_const()) { init_state->initializer_must_be_constant = TRUE; init_state->traditional_const_expr_required = curr_expr_kind_is_traditional_const(); } /* if */ init_state->not_evaluated = !curr_expr_is_evaluated(); init_state->not_potentially_evaluated = !curr_expr_is_potentially_evaluated(); } /* expr_clear_init_state */ void set_glvalue_operand_state(an_operand *operand) /* Set the state of the indicated operand to indicate that it is a glvalue. Its type must be set already. */ { /* A function designator has a special lvalue state. */ if (is_function_type(operand->type)) { operand->state = (an_operand_state)os_function_designator; } else { operand->state = (an_operand_state)os_glvalue; } /* if */ } /* set_glvalue_operand_state */ static void copy_constant_for_operand(a_constant_ptr src_constant, a_constant_ptr dst_constant) /* Copy src_constant to dst_constant using copy_constant, where dst_constant is meant to be a constant held in an_operand (either it is a pointer to a constant variant of an_operand, or it will be copied to such a variant). In addition to calling copy_constant, this also turns an indirect local expression reference into a direct reference if needed (an indirect reference would not work until the operand is transferred to the IL tree). */ { copy_constant(src_constant, dst_constant); #if PROTOTYPE_INSTANTIATIONS_IN_IL if (constant_is(dst_constant, ck_template_param) && tpck_is(dst_constant, tpck_expression) && dst_constant->variant.template_param.local_expr_ref) { /* This constant has an associated local expression node reference. Since finding it depends on having the original constant as a referrer, we have to reference the expression directly in the destination constant for now, and a new local reference will be created when the constant is transferred to the IL. */ dst_constant->variant.template_param.variant.expr = expr_node_from_tpck_expression(src_constant); dst_constant->variant.template_param.local_expr_ref = FALSE; } /* if */ #endif /* PROTOTYPE_INSTANTIATIONS_IN_IL */ } /* copy_constant_for_operand */ void make_constant_operand(a_constant *constant, an_operand *operand) /* Make a constant operand for the given constant. The position of the current token will be used as the operand position. */ { /* This routine must be callable even when there is nothing on the expression stack. */ if (is_error_type(constant->type)) { make_error_operand(operand); } else { clear_operand((an_operand_kind)ok_constant, operand); copy_constant_for_operand(constant, &operand->variant.constant); operand->type = constant->type; } /* if */ /* A string literal is an lvalue; other constants are prvalues. */ if (constant->kind == (a_constant_repr_kind)ck_string) { operand->state = (an_operand_state)os_glvalue; } else { operand->state = (an_operand_state)os_prvalue; } /* if */ set_operand_position_to_pos_curr_token(operand); } /* make_constant_operand */ struct a_constant_handle { /* A structure that wraps a pointer to a constant, but forwards equality and hashing to the pointed-to constant. This is useful for using Ptr_map to track constant values. Equality corresponds to "template argument equivalence" (see N4861, [temp.type]/2). */ a_constant_ptr ptr; /* Pointer to the represented constant. */ }; static a_boolean equiv_subobject_paths(a_subobject_path *p1, a_subobject_path *p2) /* Return TRUE if the given subobject paths are equivalent. */ { a_boolean result = TRUE; for (; p1 != NULL && p2 != NULL; p1 = p1->next, p2 = p2->next) { if (p1->is_offset != p2->is_offset || p1->is_base_class != p2->is_base_class) { break; } /* if */ if (p1->is_offset) { if (p1->variant.ptr_offset != p2->variant.ptr_offset) { break; } /* if */ } else if (p1->is_base_class) { if (!same_base_classes(p1->variant.base_class, p2->variant.base_class)) { break; } /* if */ } else { if (!same_entities(p1->variant.field, p2->variant.field)) { break; } /* if */ } /* if */ } /* for */ if (p1 != p2) { result = FALSE; } /* if */ return result; } /* equiv_subobject_paths */ static a_boolean equiv_template_arg_values(a_constant_ptr x, a_constant_ptr y, a_type_ptr tp) /* Return TRUE if the given values are "template-argument-equivalent", assuming the types and kinds have been found to be equivalent already. tp is the type of one of the constants after skip_typerefs. This is very similar to compare_constants. Note that template argument constant expressions undergo "folding" through the constexpr interpreter, which "canonicalizes" their representation. This allows this function to assume that, e.g., designators will appear equivalently in both x and y if the values have equivalent target representations (because both will have been generated by copy_interpreter_object_to_constant). */ { a_boolean result = FALSE; switch (x->kind) { case ck_error: result = FALSE; break; case ck_void: result = TRUE; break; case ck_integer: result = (cmp_integer_constants(x, y) == 0); break; #if FIXED_POINT_ALLOWED case ck_fixed_point: result = (cmp_fixed_point_constants(x, y) == 0); break; #endif /* FIXED_POINT_ALLOWED */ case ck_string: if (x->variant.string.length == y->variant.string.length) { result = (memcmp(x->variant.string.value, y->variant.string.value, size_t_arg(x->variant.string.length)) == 0); } /* if */ break; case ck_float: #if C99_IL_EXTENSIONS_SUPPORTED case ck_imaginary: #endif /* C99_IL_EXTENSIONS_SUPPORTED */ result = fp_same_representation(tp->variant.float_kind, &x->variant.float_value, &y->variant.float_value); break; #if C99_IL_EXTENSIONS_SUPPORTED case ck_complex: result = (fp_same_representation(tp->variant.float_kind, &x->variant.complex_value->real, &y->variant.complex_value->real) && fp_same_representation(tp->variant.float_kind, &x->variant.complex_value->imag, &y->variant.complex_value->imag)); break; #endif /* C99_IL_EXTENSIONS_SUPPORTED */ case ck_address: if (x->variant.address.kind == y->variant.address.kind && x->variant.address.offset == y->variant.address.offset && equiv_subobject_paths(x->variant.address.subobject_path, y->variant.address.subobject_path)) { switch (x->variant.address.kind) { case abk_routine: result = corresponding_routines( x->variant.address.variant.routine, y->variant.address.variant.routine); break; case abk_variable: result = corresponding_variables( x->variant.address.variant.variable, y->variant.address.variant.variable); break; case abk_constant: result = (x->variant.address.variant.constant == y->variant.address.variant.constant); break; case abk_temporary: result = (x->variant.address.variant.constant == y->variant.address.variant.constant); break; #if MICROSOFT_EXTENSIONS_ALLOWED case abk_uuidof: /* Microsoft __uuidof. */ { a_type_ptr uuid_type1 = x->variant.address.variant.type; a_type_ptr uuid_type2 = y->variant.address.variant.type; if (uuid_type1 == NULL && uuid_type2 == NULL) { result = TRUE; } else if (uuid_type1 == NULL || uuid_type2 == NULL) { result = FALSE; } 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); result = (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 */ result = identical_types(x->variant.address.variant.type, y->variant.address.variant.type); break; case abk_label: result = (x->variant.address.variant.label == y->variant.address.variant.label); break; case abk_param_ref: default: unexpected_condition(); } /* switch */ } /* if */ break; case ck_ptr_to_member: if (x->variant.ptr_to_member.is_function_ptr == y->variant.ptr_to_member.is_function_ptr) { if (x->variant.ptr_to_member.is_function_ptr) { result = corresponding_routines( x->variant.ptr_to_member.variant.routine, y->variant.ptr_to_member.variant.routine); } else { result = corresponding_fields( x->variant.ptr_to_member.variant.field, y->variant.ptr_to_member.variant.field); } /* if */ } /* if */ break; case ck_aggregate: { a_constant_ptr cx = x->variant.aggregate.first_constant, cy = y->variant.aggregate.first_constant; result = TRUE; for (; cx != NULL && cy != NULL; cx = cx->next, cy = cy->next) { a_type_ptr cxtp = skip_typerefs(cx->type), cytp = skip_typerefs(cy->type); if (cx->kind != cy->kind || !identical_types(cxtp, cytp) || !equiv_template_arg_values(cx, cy, cxtp)) { result = FALSE; break; } /* if */ } /* for */ if (cx != NULL || cy != NULL) result = FALSE; } break; case ck_init_repeat: { a_constant_ptr cx = x->variant.init_repeat.constant, cy = y->variant.init_repeat.constant; if (cx->kind == cy->kind && x->variant.init_repeat.count == y->variant.init_repeat.count && x->variant.init_repeat .multidimensional_aggr_tail_not_repeated == y->variant.init_repeat .multidimensional_aggr_tail_not_repeated) { a_type_ptr cxtp = skip_typerefs(cx->type), cytp = skip_typerefs(cy->type); result = identical_types(cxtp, cytp) && equiv_template_arg_values(cx, cy, cxtp); } /* if */ } break; #if GNU_EXTENSIONS_ALLOWED case ck_label_difference: { a_constant_ptr fx = x->variant.label_difference.from_address, fy = y->variant.label_difference.from_address; a_constant_ptr tx = x->variant.label_difference.to_address, ty = y->variant.label_difference.to_address; result = equiv_template_arg_values(fx, fy, skip_typerefs(fx->type)) && equiv_template_arg_values(tx, ty, skip_typerefs(fx->type)); } break; #endif /* GNU_EXTENSIONS_ALLOWED */ case ck_designator: if (x->variant.designator.is_generic || y->variant.designator.is_generic) { unexpected_condition(); } else if (x->variant.designator.is_field_designator != y->variant.designator.is_field_designator) { result = FALSE; } else { if (x->variant.designator.is_field_designator) { result = same_entities(x->variant.designator.variant.field, y->variant.designator.variant.field); } else { result = x->variant.designator.variant.array_element == y->variant.designator.variant.array_element; } /* if */ } /* if */ break; case ck_reflection: { result = eq_constants(x, y); } break; case ck_dynamic_init: #if DO_IL_LOWERING && GENERATE_EH_TABLES && !DO_FULL_PORTABLE_EH_LOWERING case ck_stack_offset: #endif /* DO_IL_LOWERING && ... */ case ck_template_param: default: unexpected_condition(); } /* switch */ return result; } /* equiv_template_arg_values */ static inline a_boolean operator==(a_constant_handle hx, a_constant_handle hy) /* Return TRUE if the constants referred to by hx and hy are "template-argument-equivalent". */ { a_constant_ptr x = hx.ptr, y = hy.ptr; a_boolean result; if (x == y) { result = TRUE; } else if (x == NULL || y == NULL) { result = FALSE; } else if (x->kind == y->kind) { a_type_ptr xtp = skip_typerefs(x->type), ytp = skip_typerefs(y->type); result = identical_types(xtp, ytp) && equiv_template_arg_values(x, y, xtp); } else { result = FALSE; } /* if */ return result; } /* operator== */ static inline a_boolean operator!=(a_constant_handle hx, a_constant_handle hy) /* Return TRUE if the constants referred to by hx and hy are not "template-argument-equivalent". */ { return !(hx == hy); } /* operator!= */ static inline uintptr_t hash_ptr(a_constant_handle h) /* Compute a hash for the given constant handle. The hash must be appropriate for Ptr_map. */ { return (uintptr_t)hash_constant(h.ptr); } /* hash_ptr */ using a_template_param_object_map = Ptr_map; /* The type of a map that associates template parameter objects (represented as constexpr variable entries) with a constant value of class type. */ STATIC_THREAD a_template_param_object_map *template_param_objects; /* A map from constant values of class types (or rather handles to such constant entries) to constexpr variable entries representing template parameter object. */ static void make_template_param_object_operand(a_constant_ptr cp, an_operand *operand, a_boolean copy_constant) /* Make operand an lvalue expression operand for the template parameter object associated with the constant cp. If needed, create that object (as a variable entry). copy_constant is TRUE if cp is a local constant (rather than an allocated IL constant): If the object is created, its value is then a copy of cp, including its subconstants (which may be shared with other constants), allocated in the file scope memory region. */ { a_constant_handle ch = { cp }; a_variable_ptr vp = template_param_objects->get(ch); if (vp == NULL) { a_symbol_ptr sym = make_template_param_object_sym(&error_position); a_type_ptr vtp = cp->type; if (copy_constant) { a_memory_region_number region_to_switch_back_to; switch_to_file_scope_region(®ion_to_switch_back_to); cp = copy_constant_full(cp, (a_constant *)NULL, CE_SRC_CONSTANT_IS_NOT_ALLOC_IN_IL); switch_back_to_original_region(region_to_switch_back_to); break_constant_source_corresp(cp); ch.ptr = cp; } /* if */ vp = make_variable(vtp, sc_unspecified, DEPTH_OF_FILE_SCOPE); vp->is_template_param_object = TRUE; vp->is_constexpr = TRUE; vp->init_kind = initk_static; vp->initializer.constant = cp; mark_inline_variable(vp, /*is_definition=*/TRUE); set_source_corresp(&vp->source_corresp, sym); template_param_objects->map(ch, vp); sym->variant.variable.ptr = vp; } /* if */ make_template_param_object_variable_operand(vp, operand); } /* make_template_param_object_operand */ void make_template_param_object_variable_operand(a_variable_ptr vp, an_operand *operand) /* Make operand a (const) lvalue expression operand for the template parameter object vp. */ { check_assertion(vp->is_template_param_object); /* Making an operand for the object is a use of it and because the object is an inline variable its definition must then be emitted here. The usual symbol reference path does not record that because the object is synthesized rather than named in the source. */ set_instance_required(symbol_for(vp), TRUE, SIR_NONE); make_lvalue_variable_operand(vp, &pos_curr_token, end_position_or_null(&end_pos_curr_token), operand, (a_ref_entry_ptr)NULL); operand->type = make_qualified_type(operand->type, TQ_CONST); operand->is_id_expression = TRUE; check_assertion(is_expression_operand(operand)); operand->variant.expression->type = operand->type; (void)make_node_from_operand(operand); } /* make_template_param_object_variable_operand */ void make_sym_constant_operand(a_symbol_ptr sym, an_operand *operand) /* Make an operand for the value of the given sk_constant symbol. It will be a prvalue usually, but a glvalue for reference-typed constants. The position of the current token will be used as the operand position. */ { a_constant *con_ptr, constant; check_assertion(symbol_is(sym, sk_constant)); con_ptr = sym->variant.constant; copy_constant_for_operand(con_ptr, &constant); break_instance_source_corresp(&constant.source_corresp); /* Detach any associated expression on template argument values accessed as the values of template parameters. */ constant.expr = NULL; if (constant_is(con_ptr, ck_error)) { /* We may get here both in error cases and in non-error cases (e.g., during template prescanning). So don't call expr_expect_error. */ make_error_operand(operand); } else if (is_reference_type(constant.type)) { /* The constant has a reference type. This happens for a constant that is an argument for a nontype template parameter that has a reference type. Make a glvalue based on that constant. */ an_expr_node_ptr expr = alloc_node_for_constant(&constant); expr = add_ref_indirection_to_node(expr); make_glvalue_expression_operand(expr, operand); } else if ((constant_is(con_ptr, ck_aggregate) || (constant_is(con_ptr, ck_template_param) && expr_stack->template_deduction_context)) && sym->is_template_param) { /* A C++20 nontype template parameter instantiated with a class type constant. Create a variable operand referring to the underlying template parameter object. */ make_template_param_object_operand(con_ptr, operand, /*copy_constant=*/FALSE); } else { /* Normal (non-reference) case. */ make_constant_operand(&constant, operand); if (sym->is_template_param && cpp20_mode && constant_is(con_ptr, ck_template_param) && (is_class_struct_union_type(con_ptr->type) || could_be_dependent_class_type(con_ptr->type))) { /* Template parameters of class type are const-qualified lvalues. */ change_template_param_constant_operand_to_lvalue( operand, !is_template_dependent_type(con_ptr->type)); } /* if */ } /* if */ } /* make_sym_constant_operand */ void make_string_constant_operand(a_constant *constant, an_operand *operand) /* Make a constant operand for the given string constant. The position of the current token will be used as the operand position. */ { /* The operand created will be an lvalue. */ make_constant_operand(constant, operand); if (!is_error_operand(operand)) { operand->is_simple_string_literal = TRUE; } /* if */ } /* make_string_constant_operand */ void make_integer_constant_operand(an_operand *operand, a_host_large_integer value) /* Make a constant operand and set it to some integer value. */ { clear_operand((an_operand_kind)ok_constant, operand); set_integer_constant(&operand->variant.constant, value, (an_integer_kind)ik_int); operand->type = operand->variant.constant.type; operand->state = (an_operand_state)os_prvalue; set_operand_position_to_pos_curr_token(operand); } /* make_integer_constant_operand */ void make_expression_operand(an_expr_node_ptr node, an_operand *operand) /* Make an expression operand for the expression "node". The operand is made a prvalue; the caller should change that if it's not appropriate (or use make_glvalue_expression_operand). This routine does not assume that the is_lvalue or is_xvalue flags of the expression are already set appropriately, and accepts that the caller might adjust them after this call returns. The position of the current token will be used as the operand position. */ { if (is_error_node(node)) { make_error_operand(operand); } else { clear_operand((an_operand_kind)ok_expression, operand); operand->type = node->type; operand->state = (an_operand_state)os_prvalue; operand->variant.expression = node; } /* if */ set_operand_position_to_pos_curr_token(operand); } /* make_expression_operand */ void make_glvalue_expression_operand(an_expr_node_ptr node, an_operand *operand) /* Make an expression operand for the expression "node". The operand is made a glvalue (either an lvalue or xvalue depending on what node is). The position of the current token will be used as the operand position. */ { check_assertion(is_glvalue_node(node) || is_error_node(node)); make_expression_operand(node, operand); set_glvalue_operand_state(operand); } /* make_glvalue_expression_operand */ void make_lvalue_or_rvalue_expression_operand(an_expr_node_ptr node, an_operand *operand) /* Make an expression operand for the expression "node". Make an lvalue, xvalue, or rvalue depending on node. */ { if (is_glvalue_node(node)) { make_glvalue_expression_operand(node, operand); } else { make_expression_operand(node, operand); } /* if */ } /* make_lvalue_or_rvalue_expression_operand */ void make_indefinite_function_operand(a_symbol_ptr routine_sym, a_symbol_locator *locator, an_operand *operand) /* Make an operand for a C++ overloaded function symbol. routine_sym points to the symbol entry for the function (possibly a projection symbol). If locator is non-NULL, that locator is used to set some additional information about the identifier. The current token position is used for the overall operand position. The operand is put into *operand and is a function designator. */ { clear_operand((an_operand_kind)ok_indefinite_function, operand); operand->state = (an_operand_state)os_function_designator; if (is_template_dependent_context() && !scope_stack_top().is_rescan && locator != NULL && locator->template_arg_list != NULL && template_arg_list_is_dependent(locator->template_arg_list)) { /* After substitution this template-id might represent a single candidate with a known type. Don't represent that with a tk_unknown type because it could lead to spurious errors during prototype instantiation. Instead, we assign a dependent unknown type to the operand. */ operand->type = type_of_unknown_templ_param_nontype; } else { operand->type = unknown_type(); } /* if */ operand->symbol = routine_sym; set_operand_position_to_pos_curr_token(operand); if (locator != NULL) { operand->is_template_id = locator->is_template_id; operand->template_arg_list = locator->template_arg_list; set_operand_id_details_from_locator(operand, locator); } else { operand->id_position = operand->position; } /* if */ } /* make_indefinite_function_operand */ void make_undefined_symbol_operand(a_symbol_ptr sym, a_ref_entry_ptr ref_list, a_symbol_locator *loc, an_operand *operand) /* Make an operand for an undefined symbol. This is a fairly transient operand that will be either turned into an implicitly-declared function (in C) or ignored (in C++, with argument_dependent lookup), or diagnosed as an error. sym is the undefined symbol. ref_list is the list of reference entries to be recorded, if any. locator describes the position and form of the source of the reference. The resulting operand is placed in *operand. */ { clear_operand((an_operand_kind)ok_undefined_symbol, operand); operand->type = unknown_type(); operand->symbol = sym; operand->ref_entries_list = ref_list; operand->id_position = loc->source_position; if (loc->is_template_id) { operand->is_template_id = TRUE; operand->template_arg_list = loc->template_arg_list; } /* if */ } /* make_undefined_symbol_operand */ void make_sym_for_member_operand(a_symbol_ptr member_sym, a_boolean is_qualified_name, a_ref_entry_ptr rep, an_operand *operand) /* Make an operand for a nonstatic class member name. Used in C++ to represent a member name per se, that is, to represent an uninterpreted member name from when it is scanned until it becomes clear from the context how the member is being used. Not used for overloaded functions. member_sym points to the symbol entry for the member (possibly a projection symbol). is_qualified_name is TRUE if the name was qualified, e.g., "A::f" instead of just "f". rep points to an associated reference entry, or is NULL if none is needed. The operand is put into *operand. It is a function designator if the symbol is a function, an rvalue otherwise. */ { a_symbol_ptr fund_sym = fundamental_symbol_of(member_sym); clear_operand((an_operand_kind)ok_sym_for_member, operand); if (fund_sym->kind == (a_symbol_kind)sk_field) { /* Data member. */ operand->state = (an_operand_state)os_prvalue; operand->type = fund_sym->variant.field.ptr->type; } else { check_assertion(fund_sym->kind == (a_symbol_kind)sk_member_function); /* Member function. */ operand->state = (an_operand_state)os_function_designator; operand->type = fund_sym->variant.routine.ptr->type; #if USE_X86_FUNCTION_MULTIVERSIONING if (gpp_mode && is_multiversion_representative(fund_sym->variant.routine.ptr)) { /* Record the use of a GNU function multiversion member function (and report any errors). */ reference_to_mv_routine(fund_sym->variant.routine.ptr, &pos_curr_token); } /* if */ #endif /* USE_X86_FUNCTION_MULTIVERSIONING */ } /* if */ operand->symbol = member_sym; operand->is_qualified_name = is_qualified_name; set_operand_position_to_pos_curr_token(operand); operand->ref_entries_list = rep; } /* make_sym_for_member_operand */ void make_template_param_expr_constant_operand(an_operand *operand) /* operand is a template-dependent operand that must eventually have a constant value. Change it to an operand for a ck_template_param constant for the underlying expression. This makes a constant of subkind tpck_expression. Return the operand in *result. */ { a_constant_ptr con = local_constant(); an_operand orig_operand; orig_operand = *operand; /* Build the ck_template_param constant. */ make_template_param_constant_from_operand(operand, con, (a_type_ptr)NULL); /* Make the operand. */ make_constant_operand(con, operand); restore_operand_details(operand, &orig_operand); /* Retain the original operand state, as doing otherwise may confuse overload resolution for value-dependent operands. */ operand->state = orig_operand.state; release_local_constant(&con); } /* make_template_param_expr_constant_operand */ static void expr_binary_operation(an_expr_operator_kind op, a_constant *constant_1, a_constant *constant_2, a_type_ptr result_type, a_constant *result, a_boolean *did_not_fold, a_boolean *template_constant, a_source_position *err_pos) /* Interface to binary_operation (which folds a binary operator applied to two constant operands) for use within the expression processing routines. Provides some of the parameters from information in the expression stack. */ { an_error_code error_detected = ec_no_error, *p_error_detected = NULL; if (expr_stack->suppress_diagnostics) { p_error_detected = &error_detected; } /* if */ binary_operation(op, constant_1, constant_2, result_type, result, curr_expr_kind_is_const(), curr_expr_is_evaluated(), did_not_fold, template_constant, p_error_detected, err_pos); if (error_detected != ec_no_error) { /* Record that an error occurred while diagnostics are suppressed. This can, for example, cause template deduction to fail later. */ record_suppressed_error(); } /* if */ } /* expr_binary_operation */ static void expr_unary_operation(an_expr_operator_kind op, a_constant *constant, a_type_ptr result_type, a_constant *result, a_boolean *did_not_fold, a_boolean *template_constant, a_source_position *err_pos) /* Interface to unary_operation (which folds a unary operator applied to one constant operand) for use within the expression processing routines. Provides some of the parameters from information in the expression stack. */ { an_error_code error_detected = ec_no_error, *p_error_detected = NULL; if (expr_stack->suppress_diagnostics) { p_error_detected = &error_detected; } /* if */ unary_operation(op, constant, result_type, result, curr_expr_kind_is_const(), curr_expr_is_evaluated(), did_not_fold, template_constant, p_error_detected, err_pos); if (error_detected != ec_no_error) { /* Record that an error occurred while diagnostics are suppressed. This can, for example, cause template deduction to fail later. */ record_suppressed_error(); } /* if */ } /* expr_unary_operation */ static void expr_type_change_constant(a_constant *constant, a_type_ptr new_type, a_boolean is_implicit_cast, a_boolean check_cast_access, a_boolean check_ambiguity, a_boolean is_reinterpret_cast, a_boolean maintain_expression, a_boolean *did_not_fold, a_source_position *err_pos) /* Interface to type_change_constant_full (which casts a constant to a new type) for use within the expression processing routines. Provides some of the parameters from information in the expression stack. */ { an_error_code error_detected = ec_no_error, *p_error_detected = NULL; if (expr_stack->suppress_diagnostics) { p_error_detected = &error_detected; } /* if */ type_change_constant_full(constant, new_type, is_implicit_cast, curr_expr_kind_is_const(), curr_expr_is_evaluated(), (a_boolean)expr_stack->favor_constant_result, curr_expr_is_cli_attribute_argument(), check_cast_access, check_ambiguity, is_reinterpret_cast, maintain_expression, did_not_fold, p_error_detected, err_pos); if (error_detected != ec_no_error) { /* Record that an error occurred while diagnostics are suppressed. This can, for example, cause template deduction to fail later. */ record_suppressed_error(); } /* if */ } /* expr_type_change_constant */ void add_base_class_casts(a_base_class_ptr bcp, a_type_ptr qualifiers_model, a_boolean check_cast_access, a_boolean check_ambiguity, a_boolean allow_ambiguity, a_boolean is_implicit_cast, a_boolean implicit_in_naming, an_expr_node_ptr *p_node, a_source_position *err_pos, a_boolean *error_detected) /* Add casts to *p_node to change its type from (a pointer to) a class type to (a pointer to) a base class of that class; bcp indicates the base class and qualifiers_model indicates the qualifiers to be placed on that class type. *p_node can be a prvalue pointer to class, or a class lvalue, xvalue, or rvalue. In C++/CLI mode, it can also be a handle to a class. qualifiers_model is a potentially cv-qualified class type. Access control is done on the cast if check_cast_access is TRUE. Checking for an ambiguous base class is done if check_ambiguity is TRUE. allow_ambiguity controls whether the diagnostic that is issued for an ambiguity is a warning or an error. is_implicit_cast is TRUE if the cast is implicit. implicit_in_naming is TRUE for casts that are generated implicitly in referencing a member of a class (roughly, in getting from the name used in the source -- the projection symbol -- to the member actually used in the IL). *err_pos indicates a source position to be used for errors. If error_detected is non-NULL, return *error_detected set to TRUE if there was an error, and do not issue any diagnostics (including warnings). Note that calls from outside the expression-processing routines must specify error_detected != NULL. For calls from inside, this routine does handle suppression of errors in deduction contexts appropriately and error_detected can be NULL. */ { a_type_ptr curr_type, qual_curr_type, orig_type; a_derivation_step_ptr dsp, tail; a_base_class_ptr base_class; a_boolean pointer_case = FALSE; /* The code here looks like fold_base_class_cast. */ if (error_detected != NULL) *error_detected = FALSE; check_assertion(is_class_struct_union_type(qualifiers_model)); if (!base_class_cast_access_checking_should_be_done()) { check_cast_access = FALSE; } /* if */ /* When allow_ambiguity is TRUE, only issue a warning for ambiguous cases. bcp is expected to point to the base class to be used. This is used in Microsoft bugs mode where ambiguous base classes are allowed in some cases. */ if (bcp->ambiguous && check_ambiguity && !allow_ambiguity) { /* The cast is ambiguous. */ if (error_detected != NULL) { *error_detected = TRUE; } else if (expr_error_should_be_issued()) { pos_ty_error(ec_ambiguous_base_class, err_pos, bcp->type); } /* if */ *p_node = error_node(); } else { /* An ambiguous base class is allowed (with a warning) in some cases in Microsoft bugs mode. The actual base class used is normally the first one found, but in some cases (see find_base_class_of_full) a direct base class is preferred. */ if (bcp->ambiguous && check_ambiguity && allow_ambiguity) { pos_ty_warning(ec_ambiguous_base_class, err_pos, bcp->type); } /* if */ /* Loop through the classes between the derived class and the base class. Check accessibility at each step and generate the necessary casts. */ curr_type = orig_type = (*p_node)->type; if (is_pointer_or_handle_type(curr_type)) { pointer_case = TRUE; curr_type = type_pointed_to(curr_type); } /* if */ curr_type = skip_typerefs(curr_type); check_assertion(is_immediate_class_type(curr_type)); /* Note that dsp can be NULL in the case of a dummy base class invented for a projection of a member of a nonreal class into another class, e.g., via a using-declaration. */ if (bcp->derivation == NULL) { dsp = NULL; tail = NULL; } else { dsp = cast_derivation_path_of(bcp); tail = bcp->derivation->path_tail; } /* if */ for (;;) { base_class = (dsp != NULL) ? dsp->base_class : bcp; /* Check that the base class is accessible from the current class. */ if (check_cast_access && dsp != NULL) { if (!is_accessible_imm_base_class(base_class, curr_type, bcp)) { /* The base class is inaccessible. Keep going, but issue the error only once. */ if (error_detected != NULL) { *error_detected = TRUE; } else if (expr_diagnostic_should_be_issued( es_discretionary_error, ec_inaccessible_base_class, err_pos)) { pos_ty_diagnostic(es_discretionary_error, ec_inaccessible_base_class, err_pos, base_class->type); } /* if */ check_cast_access = FALSE; } /* if */ } /* if */ /* Add the cast to the next level. */ curr_type = base_class->type; /* The type should have all the qualifiers of the qualifiers_model type. */ qual_curr_type = make_identically_qualified_type(curr_type, qualifiers_model); if (pointer_case) { qual_curr_type = make_pointer_type_of_same_kind(qual_curr_type, orig_type); } /* if */ { an_expr_node_ptr curr_node = *p_node; an_expr_node_ptr new_node; if (!pointer_case && !is_glvalue_node(curr_node)) { curr_node = glvalue_from_class_prvalue_node(curr_node, /*is_xvalue=*/TRUE); } /* if */ new_node = make_operator_node( (an_expr_operator_kind)eok_base_class_cast, qual_curr_type, curr_node); new_node->position = *err_pos; *p_node = new_node; copy_node_value_category(curr_node, new_node); if (is_implicit_cast) { new_node->compiler_generated = TRUE; } /* if */ new_node->variant.operation.implicit_in_member_naming = implicit_in_naming; if (!is_implicit_cast && dsp != NULL && dsp != tail) { new_node->variant.operation.implicit_step_of_explicit_cast = TRUE; } /* if */ } if (dsp == NULL) break; dsp = dsp->next; if (dsp == tail->next) break; } /* for */ } /* if */ } /* add_base_class_casts */ static void add_a_derived_class_cast( a_type_ptr new_type_pointed_to, a_derivation_path path, a_boolean requires_runtime_check, a_source_position_ptr src_pos, an_expr_node_ptr *p_node) /* Helper routine for add_derived_class_casts: adds casts to *p_node to change its type to pointer to new_type_pointed_to. path denotes the derivation list from the desired type to the current type (i.e., it's backwards from what's needed). requires_runtime_check is TRUE if a C++/CLI runtime check is needed. *src_pos indicates the source position to record for the cast (and to issue any errors). Also handles casting of class lvalues and rvalues. */ { a_type_ptr cast_type; a_derivation_step_ptr dsp; /* Iterate backwards over the path. Add casts to get the type we have to the type just below the type we want. */ for (dsp = path.tail; dsp != path.head->prev; dsp = dsp->prev) { if (dsp != path.head) { cast_type = dsp->prev->base_class->type; cast_type = make_identically_qualified_type(cast_type, new_type_pointed_to); } else { cast_type = new_type_pointed_to; } /* if */ if (is_pointer_or_handle_type((*p_node)->type)) { cast_type = make_pointer_type_of_same_kind(cast_type, (*p_node)->type); } /* if */ /* Add the node to do the cast. */ { an_expr_node_ptr curr_node = *p_node; an_expr_node_ptr new_node = *p_node = make_operator_node(eok_derived_class_cast, cast_type, *p_node); new_node->position = *src_pos; copy_node_value_category(curr_node, new_node); #if MICROSOFT_EXTENSIONS_ALLOWED if (requires_runtime_check) { new_node->variant.operation.requires_runtime_cast_check = TRUE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (dsp != path.head) { new_node->variant.operation.implicit_step_of_explicit_cast = TRUE; } /* if */ /* No need to set compiler_generated; a derived class cast is always explicit. */ } } /* for */ } /* add_a_derived_class_cast */ void add_derived_class_casts(a_type_ptr new_type_pointed_to, a_base_class_ptr bcp, a_boolean check_ambiguity, a_boolean requires_runtime_check, an_expr_node_ptr *p_node, a_source_position *err_pos, a_boolean *error_detected) /* Add casts to *p_node to change its type from pointer to a class type to pointer to new_type_pointed_to, a derived class of that class; bcp indicates the base class of the derived class that corresponds to the current type (i.e., its derivation list is backwards from what's needed). Also handles casting of class lvalues and rvalues. check_ambiguity is TRUE if checking for an ambiguous class should be done. requires_runtime_check is TRUE if a C++/CLI runtime check is needed. *err_pos indicates a source position to be used for errors. If error_detected is non-NULL, return *error_detected set to TRUE if there was an error, and do not issue any diagnostics (including warnings). Note that calls from outside the expression-processing routines must specify error_detected != NULL. For calls from inside, this routine does handle suppression of errors in deduction contexts appropriately and error_detected can be NULL. */ { /* The code here looks like fold_derived_class_cast. */ if (error_detected != NULL) *error_detected = FALSE; if (bcp->ambiguous && check_ambiguity) { /* The cast is ambiguous. */ if (error_detected != NULL) { *error_detected = TRUE; } else { expr_pos_ty2_error(ec_ambiguous_derived_class, err_pos, new_type_pointed_to, bcp->type); } /* if */ *p_node = error_node(); } else if (any_virtual_steps_in_derivation(bcp) #if MICROSOFT_EXTENSIONS_ALLOWED && !is_managed_class_type(bcp->type) #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ ) { /* The base class is a virtual base of the derived class, or there's a virtual step on the derivation path. */ if (error_detected != NULL) { *error_detected = TRUE; } else { expr_pos_ty2_error(ec_derived_class_from_virtual_base, err_pos, new_type_pointed_to, bcp->type); } /* if */ *p_node = error_node(); } else { /* Use recursion to process the list backwards to generate casts. */ add_a_derived_class_cast(new_type_pointed_to, { cast_derivation_path_of(bcp), bcp->derivation->path_tail }, requires_runtime_check, err_pos, p_node); } /* if */ } /* add_derived_class_casts */ static void add_pm_base_class_casts(a_type_ptr new_type, a_base_class_ptr bcp, a_boolean check_ambiguity, an_expr_node_ptr *p_node, a_source_position *err_pos) /* Add casts to *p_node to change its type from a pointer to a member of a class type to a pointer to a member as given by new_type, based on a base class of that class; bcp indicates the base class. Check for an ambiguous base class if check_ambiguity is TRUE. *err_pos indicates a source position to be used for errors. This routine is only used in C++ mode. Note that casts of this type always come from explicit casts, so checking for accessibility of base classes is not necessary. */ { a_type_ptr member_type = pm_member_type((*p_node)->type); a_derivation_step_ptr dsp; /* The code here looks like fold_pm_base_class_cast. */ if (bcp->ambiguous && check_ambiguity) { /* The cast is ambiguous. */ if (expr_error_should_be_issued()) { pos_ty_error(ec_ambiguous_base_class, err_pos, bcp->type); } /* if */ *p_node = error_node(); } else if (any_virtual_steps_in_derivation(bcp) && !any_cfront_mode()) { /* The base class is a virtual base of the derived class, or there's a virtual step on the derivation path. */ expr_pos_ty2_error(ec_pm_virtual_base_from_derived_class, err_pos, pm_class_type((*p_node)->type), bcp->type); *p_node = error_node(); } else { a_derivation_step_ptr tail = bcp->derivation->path_tail; /* Loop through the classes between the derived class and the base class. Generate the necessary casts. */ for (dsp = cast_derivation_path_of(bcp); dsp != tail->next; dsp = dsp->next) { /* Add the cast to the next level. */ a_type_ptr step_type; if (dsp != tail) { a_type_ptr curr_type = dsp->base_class->type; step_type = related_ptr_to_member_type(member_type, curr_type); } else { step_type = new_type; } /* if */ *p_node = make_operator_node( (an_expr_operator_kind)eok_pm_base_class_cast, step_type, *p_node); /* No need to set compiler_generated; this cast cannot be implicit. */ if (dsp != tail) { (*p_node)->variant.operation.implicit_step_of_explicit_cast = TRUE; } /* if */ } /* for */ } /* if */ } /* add_pm_base_class_casts */ static void add_a_pm_derived_class_cast( a_type_ptr new_class_pointed_to, a_derivation_path path, a_boolean is_implicit_cast, an_expr_node_ptr *p_node) /* Helper routine for add_pm_derived_class_casts: adds casts to *p_node to change its type to pointer to member of new_class_pointed_to. path denotes the derivation list from the desired type to the current type (i.e., it's backwards from what's needed). is_implicit_cast is TRUE if the cast is implicit. */ { a_derivation_step_ptr dsp; /* Iterate backwards over the path. Add casts to get the type we have to the type just below the type we want. */ for (dsp = path.tail; dsp != path.head->prev; dsp = dsp->prev) { a_type_ptr class_type = dsp == path.head ? new_class_pointed_to : dsp->prev->base_class->type; a_type_ptr member_type = pm_member_type((*p_node)->type); an_expr_node *new_node; /* Add the node to do the cast. */ new_node = make_operator_node(eok_pm_derived_class_cast, related_ptr_to_member_type(member_type, class_type), *p_node); new_node->position = (*p_node)->position; new_node->compiler_generated = is_implicit_cast; *p_node = new_node; if (dsp != path.head && !is_implicit_cast) { check_assertion(is_operation_node(*p_node) && (*p_node)->variant.operation.kind == eok_pm_derived_class_cast); (*p_node)->variant.operation.implicit_step_of_explicit_cast = TRUE; } /* if */ } /* for */ } /* add_a_pm_derived_class_cast */ static void add_pm_derived_class_casts(a_type_ptr new_type, a_base_class_ptr bcp, a_boolean check_cast_access, a_boolean check_ambiguity, a_boolean is_implicit_cast, an_expr_node_ptr *p_node, a_source_position *err_pos) /* Add casts to *p_node to change its type from pointer to member of a class type to a pointer to a member as given by new_type, based on a derived class of that class; bcp indicates the base class of the derived class that corresponds to the current type (i.e., its derivation list is backwards from what's needed). Do access control on the cast if check_cast_access is TRUE. Check for an ambiguous class if check_ambiguity is TRUE. is_implicit_cast is TRUE if the cast is implicit. *err_pos indicates a source position to be used for errors. This routine is only used in C++ mode. */ { a_type_ptr curr_type; a_derivation_step_ptr dsp; a_base_class_ptr base_class; a_type_ptr new_class_pointed_to = pm_class_type(new_type); /* The code here looks like fold_pm_derived_class_cast. */ if (!expr_access_checking_should_be_done()) check_cast_access = FALSE; if (bcp->ambiguous && check_ambiguity) { /* The cast is ambiguous. */ expr_pos_ty2_error(ec_ambiguous_derived_class, err_pos, new_class_pointed_to, bcp->type); *p_node = error_node(); } else if (!(microsoft_mode && PTR_TO_MEMBER_REPR_SUPPORTS_CAST_FROM_VIRTUAL_BASE) && /*lint !e506*/ any_virtual_steps_in_derivation(bcp)) { /* The base class is a virtual base of the derived class, or there's a virtual step on the derivation path. */ expr_pos_ty2_error(ec_pm_derived_class_from_virtual_base, err_pos, new_class_pointed_to, bcp->type); *p_node = error_node(); } else { if (check_cast_access) { a_derivation_step_ptr tail = bcp->derivation->path_tail; /* Check the accessibility of the base class. (Recall that casts to derived types can be done implicitly.) */ curr_type = new_class_pointed_to; for (dsp = cast_derivation_path_of(bcp); dsp != tail->next; dsp = dsp->next) { base_class = dsp->base_class; /* Check that the base class is accessible from the current class. */ if (!is_accessible_imm_base_class(base_class, curr_type, bcp)) { if (expr_diagnostic_should_be_issued( es_discretionary_error, ec_conv_from_inaccessible_base_class, err_pos)) { pos_ty_diagnostic(es_discretionary_error, ec_conv_from_inaccessible_base_class, err_pos, base_class->type); } /* if */ break; } /* if */ curr_type = base_class->type; } /* for */ } /* if */ /* Use recursion to process the list backwards to generate casts. */ add_a_pm_derived_class_cast(new_class_pointed_to, { cast_derivation_path_of(bcp), bcp->derivation->path_tail }, is_implicit_cast, p_node); check_assertion(is_operation_node(*p_node) && (*p_node)->variant.operation.kind == (an_expr_operator_kind)eok_pm_derived_class_cast); /* Store the final type, which may differ from what's there now in having different cv-qualifiers on the member type. */ (*p_node)->type = new_type; } /* if */ } /* add_pm_derived_class_casts */ #if MICROSOFT_EXTENSIONS_ALLOWED an_expr_node_ptr add_box_to_expression(an_expr_node_ptr expr, a_boolean is_implicit, a_boolean handle_to_form) /* Add a C++/CLI "box" operation to the indicated expression (a prvalue with a boxable CLI value type), and return the boxed expression. is_implicit is TRUE if the boxing is implicit (as opposed to coming from an explicit cast). If handle_to_form is TRUE, the source form uses the unary "%" operator. */ { a_type_ptr boxed_type = make_unqualified_type(expr->type); check_assertion(!is_glvalue_node(expr) && is_boxable_type(expr->type)); check_assertion(!(is_implicit && handle_to_form)); expr = make_operator_node((handle_to_form ? (an_expr_operator_kind)eok_handle_to_box : (an_expr_operator_kind)eok_box), make_handle_type(boxed_type), expr); expr->compiler_generated = is_implicit; return expr; } /* add_box_to_expression */ an_expr_node_ptr add_unbox_to_expression(an_expr_node_ptr expr, a_type_ptr unboxed_type, a_boolean make_lvalue) /* Add a C++/CLI "unbox" operation to the indicated expression (a prvalue with a handle type), and return the unboxed expression. unboxed_type gives the desired unboxed type (which may have cv-qualifiers, and might be a derived class of the underlying type of the handle). If make_lvalue is TRUE, an lvalue expression is returned. */ { check_assertion(!is_glvalue_node(expr) && is_handle_type(expr->type)); if (make_lvalue) { expr = make_lvalue_operator_node((an_expr_operator_kind)eok_unbox, unboxed_type, expr); } else { expr = make_operator_node((an_expr_operator_kind)eok_unbox, unboxed_type, expr); } /* if */ return expr; } /* add_unbox_to_expression */ an_expr_node_ptr unbox_after_indirection_if_required(an_expr_node_ptr expr) /* expr is the result of applying a "*" indirection operator. If the result is a C++/CLI value class that has a corresponding fundamental type, add an unbox operation to the expression to get an lvalue for the fundamental type value within the boxed entity. Return the updated expression, or the original expression if no change was needed. */ { if (cli_or_cx_enabled) { a_type_ptr fund_type = fundamental_type_from_system_type(f_skip_typerefs(expr->type)); if (fund_type != NULL) { a_type_ptr unboxed_type = type_plus_qualifiers_from_second_type(fund_type, expr->type); check_assertion(expr->is_lvalue); expr = make_lvalue_operator_node((an_expr_operator_kind)eok_unbox_lvalue, unboxed_type, expr); expr->compiler_generated = TRUE;; } /* if */ } /* if */ return expr; } /* unbox_after_indirection_if_required */ static a_boolean expr_is_literal_convertible_to_cli_string( an_expr_node_ptr expr) /* Return TRUE if the given expression is a string literal, or an expression made up of "?" and "," operations whose leaf value nodes are all string literals, that is convertible to a C++/CLI System::String^. */ { a_boolean is_string_lit = FALSE; expr = skip_parens(expr); if (!literal_type_convertible_to_cli_string(expr->type)) { /* Wrong type, so look no further. */ } else if (expr_is_pointer_to_string_literal(expr, (a_constant **)NULL)) { /* The expression is a string literal that has decayed to a pointer. */ is_string_lit = TRUE; } else if (is_constant_node(expr)) { if (node_constant_is(expr, ck_string)) { /* The node is an undecayed string literal. */ is_string_lit = TRUE; } /* if */ } else if (is_operation_node(expr) && node_operator_is(expr, eok_question)) { /* A "?" operator is okay if the second and third operands are string literals. */ an_expr_node_ptr op1 = expr->variant.operation.operands; an_expr_node_ptr op2 = op1->next; an_expr_node_ptr op3 = op2->next; if (expr_is_literal_convertible_to_cli_string(op2) && expr_is_literal_convertible_to_cli_string(op3)) { is_string_lit = TRUE; } /* if */ } else if (is_operation_node(expr) && node_operator_is(expr, eok_comma)) { /* Similarly for a "," operator: check the second operand. */ an_expr_node_ptr op1 = expr->variant.operation.operands; an_expr_node_ptr op2 = op1->next; if (expr_is_literal_convertible_to_cli_string(op2)) { is_string_lit = TRUE; } /* if */ } /* if */ return is_string_lit; } /* expr_is_literal_convertible_to_cli_string */ static a_boolean operand_is_pointer_to_string_literal(an_operand *operand) /* Return TRUE if the operand is a pointer to a string literal (i.e., a string literal that has decayed to or been cast to a pointer). */ { a_boolean is_string_lit = FALSE; if (is_constant_operand(operand)) { if (constant_is_pointer_to_string_literal(&operand->variant.constant, (a_constant **)NULL)) { is_string_lit = TRUE; } /* if */ } else if (is_expression_operand(operand)) { if (expr_is_pointer_to_string_literal(operand->variant.expression, (a_constant **)NULL)) { is_string_lit = TRUE; } /* if */ } /* if */ return is_string_lit; } /* operand_is_pointer_to_string_literal */ a_boolean is_literal_convertible_to_cli_string(an_operand *operand, a_boolean allow_complex) /* Return TRUE in C++/CLI mode if the indicated operand is a string literal that can be converted to System::String^. If allow_complex is TRUE, also return TRUE for "?" and "," expressions where the leaf value nodes are all string literals, e.g., x ? "abc" : "de". */ { a_boolean convertible = FALSE; if (cli_or_cx_enabled && operand->is_simple_string_literal && literal_type_convertible_to_cli_string(operand->type) && /* The is_simple_string_literal flag is set for "?" expressions like x ? "abc" : "de" to allow the deprecated conversion from const string literal to char *. Make sure we really have a string literal case here. */ (operand_is_string_literal(operand) || operand_is_pointer_to_string_literal(operand))) { convertible = TRUE; } else if (allow_complex && is_expression_operand(operand) && expr_is_literal_convertible_to_cli_string( operand->variant.expression)) { convertible = TRUE; } /* if */ return convertible; } /* is_literal_convertible_to_cli_string */ static an_expr_node_ptr convert_expr_to_handle_to_cli_string( an_expr_node_ptr expr) /* Helper function for convert_operand_to_handle_to_cli_string. Convert the string literals in the given expression to System::Strings. Return the rewritten expression. */ { a_constant_ptr string_constant = local_constant(); #if PARENS_IN_IL an_expr_node_ptr orig_expr = expr; an_expr_node_ptr under_parens = (expr = skip_parens(expr)); #endif /* PARENS_IN_IL */ if (!expr->is_lvalue && is_pointer_type(expr->type) && constant_prvalue_pointer(expr, string_constant, /*address_escapes=*/TRUE)) { /* A constant string literal: set the type to System::String^ and create the constant node. */ an_expr_node_ptr cli_string_node; string_constant->type = make_handle_to_system_string(); cli_string_node = alloc_node_for_constant(string_constant); /* Copy the position information from the original constant node, skipping over any compiler-generated nodes like array-decay, which have no source position. */ while (is_operation_node(expr) && expr->compiler_generated) { expr = expr->variant.operation.operands; } /* while */ cli_string_node->position = expr->position; #if EXTRA_SOURCE_POSITIONS_IN_IL cli_string_node->expr_range = expr->expr_range; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ expr = cli_string_node; } else if (is_operation_node(expr) && node_operator_is(expr, eok_question)) { /* For a "?" operator, transform the second and third operands. Because of the way is_literal_convertible_to_cli_string works, when we get to the leaf nodes that need to be transformed they will always be string literals. */ an_expr_node_ptr op1 = expr->variant.operation.operands; an_expr_node_ptr op2 = op1->next; an_expr_node_ptr op3 = op2->next; op2 = convert_expr_to_handle_to_cli_string(op2); op3 = convert_expr_to_handle_to_cli_string(op3); op1->next = op2; op2->next = op3; expr->type = make_handle_to_system_string(); } else if (is_operation_node(expr) && node_operator_is(expr, eok_comma)) { /* Similarly for a "," operator: transform the second operand. */ an_expr_node_ptr op1 = expr->variant.operation.operands; an_expr_node_ptr op2 = op1->next; op2 = convert_expr_to_handle_to_cli_string(op2); op1->next = op2; expr->type = make_handle_to_system_string(); } else if (is_error_node(expr)) { /* Return an error node unchanged. */ } else { unexpected_condition(); } /* if */ #if PARENS_IN_IL if (orig_expr != under_parens) { /* Put the original parenthesis node(s) back on top of the expression. */ an_expr_node_ptr node = orig_expr, next_node; for (;;) { check_assertion(is_operation_node(node) && node_operator_is(node, eok_parens)); node->type = expr->type; next_node = node->variant.operation.operands; if (next_node == under_parens) break; node = next_node; } /* for */ node->variant.operation.operands = expr; expr = orig_expr; } /* if */ #endif /* PARENS_IN_IL */ release_local_constant(&string_constant); return expr; } /* convert_expr_to_handle_to_cli_string */ void convert_operand_to_handle_to_cli_string(an_operand_ptr operand) /* Convert operand (a string literal) to System::String^. The result will be a constant operand designating an address constant that refers to the input string literal. Note that such a "constant" actually involves a run-time operation (the allocation and construction of a System::String object in the gc-heap), but this is treated as a compile-time constant by the Microsoft compiler, hence the unusual representation. Also handles cases where there are string literals under a "?" or "," operator, e.g., in x ? "abc" : "de" the two string literal nodes are converted to System::String^. The expression should be one for which is_literal_convertible_to_cli_string is TRUE. */ { a_constant_ptr string_constant = local_constant(); an_operand orig_operand; an_expr_node_ptr expr; #if EXPENSIVE_CHECKING check_assertion(is_literal_convertible_to_cli_string(operand, /*allow_complex=*/TRUE)); #endif /* EXPENSIVE_CHECKING */ orig_operand = *operand; /* Convert lvalue string literals to prvalue pointers. */ do_operand_transformations(operand, TOPT_NO_OPTIONS); expr = make_node_from_operand(operand); if (!expr->is_lvalue && is_pointer_type(expr->type) && constant_prvalue_pointer(expr, string_constant, /*address_escapes=*/TRUE)) { /* The operand is a simple string literal, so make a constant operand for the corresponding handle. */ string_constant->type = make_handle_to_system_string(); make_constant_operand(string_constant, operand); } else { /* Handle cases like x ? "abc" : "de". */ expr = convert_expr_to_handle_to_cli_string(expr); make_expression_operand(expr, operand); } /* if */ restore_operand_details(operand, &orig_operand); release_local_constant(&string_constant); } /* convert_operand_to_handle_to_cli_string */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ void add_cast_to_node(an_expr_node_ptr *p_node, a_type_ptr new_type, a_boolean check_cast_access, a_boolean check_ambiguity, a_boolean is_implicit_cast, a_boolean is_reinterpret_cast, a_boolean reinterpret_semantics, a_source_position *err_pos) /* Add a cast node to the expression tree pointed to by *p_node, and update *p_node to point to the cast node. The old node is cast to the type new_type. *err_pos gives the source position for errors. Check access on the cast if check_cast_access is TRUE. Check for an ambiguous base or derived class if check_ambiguity is TRUE. If is_implicit_cast is TRUE, this is an implicit cast rather than an explicit one. This routine generates the special IL operators used for base-->derived and derived-->base class pointer casts, when they are appropriate. It also issues errors for invalid casts of that kind (e.g., ambiguous). If reinterpret_semantics is TRUE, those related class casts are not checked for. is_reinterpret_cast indicates that the cast comes from a reinterpret_cast construct in the source. */ { a_type_ptr old_type = (*p_node)->type, new_type_pointed_to; a_boolean baseward_cast; a_base_class_ptr bcp; #if MICROSOFT_EXTENSIONS_ALLOWED an_expr_node_ptr expr; a_boolean check_need_for_final_cast = FALSE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Make sure the next field of the node is cleared. The caller must make sure it's copied if that's necessary (it can't be done here, because we can't link a previous expression in a list to this new expression). */ (*p_node)->next = NULL; if (!expr_access_checking_should_be_done()) check_cast_access = FALSE; if (!C_mode() && !reinterpret_semantics && related_class_pointers_or_handles(old_type, new_type, &baseward_cast, &bcp)) { LINT_IS_INITIALIZED(baseward_cast); LINT_IS_INITIALIZED(bcp); /* C++ cast from a pointer to a class to a pointer to a related (base or derived) class. */ new_type_pointed_to = type_pointed_to(new_type); if (baseward_cast) { /* Derived --> base. Valid unless the cast is ambiguous or the base class is inaccessible. */ add_base_class_casts(bcp, new_type_pointed_to, check_cast_access, check_ambiguity, /*allow_ambiguity=*/FALSE, is_implicit_cast, /*implicit_in_naming=*/FALSE, p_node, err_pos, (a_boolean *)NULL); } else { /* Base --> derived. Valid unless the cast is ambiguous or the base class is a virtual base of the derived class. */ add_derived_class_casts(new_type_pointed_to, bcp, check_ambiguity, /*requires_runtime_check=*/FALSE, p_node, err_pos, (a_boolean *)NULL); } /* if */ } else if (!C_mode() && !reinterpret_semantics && related_member_pointers(old_type, new_type, &baseward_cast, &bcp)) { /* C++ cast from pointer-to-member to pointer-to-member-of-related-class. Note that the underlying member types may be different. */ if (baseward_cast) { /* Derived --> base (allowed only as an explicit cast). Valid unless the cast is ambiguous. */ add_pm_base_class_casts(new_type, bcp, check_ambiguity, p_node, err_pos); } else { /* Base --> derived (allowed as an implicit or explicit cast). Valid unless the cast is ambiguous, the base class is inaccessible (if the cast is implicit), or the base class is a virtual base of the derived class. */ add_pm_derived_class_casts(new_type, bcp, check_cast_access, check_ambiguity, is_implicit_cast, p_node, err_pos); } /* if */ } else if ((!C_mode() || c99_mode || gcc_mode) && (is_bool_type(new_type) || enum_has_bool_underlying_type(new_type))) { /* Cast to bool, or to an enumeration whose underlying type is bool. Use eok_bool_cast to normalize the operand to false/true. For such an enumeration the value is first converted to the underlying bool type (per N5014 [expr.static.cast]/7,8), and the bool result is then cast to the enumeration type. */ a_boolean to_bool_enum = !is_bool_type(new_type); *p_node = make_operator_node(eok_bool_cast, to_bool_enum ? bool_type() : new_type, *p_node); if (is_implicit_cast) { (*p_node)->compiler_generated = TRUE; (*p_node)->position = *err_pos; } /* if */ if (to_bool_enum) { *p_node = make_operator_node(eok_cast, new_type, *p_node); (*p_node)->variant.operation.is_reinterpret_cast = FALSE; (*p_node)->variant.operation.is_reinterpret_like_cast = FALSE; if (is_implicit_cast) { (*p_node)->compiler_generated = TRUE; (*p_node)->position = *err_pos; } /* if */ } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED } else if (cli_or_cx_enabled && boxing_conversion_possible(old_type, new_type, (a_std_conv_descr *)NULL)) { /* Do a boxing conversion. */ check_assertion(!is_reinterpret_cast); (*p_node) = add_box_to_expression(*p_node, is_implicit_cast, /*handle_to_form=*/FALSE); if (!cast_identical_types(new_type, (*p_node)->type)) { check_need_for_final_cast = TRUE; check_assertion(is_operation_node(*p_node) && node_operator_is(*p_node, eok_box)); (*p_node)->compiler_generated = FALSE; } /* if */ } else if (cli_or_cx_enabled && unboxing_conversion_possible(old_type, new_type, (a_std_conv_descr *)NULL)) { /* Do an unboxing conversion. */ check_assertion(!is_implicit_cast && !is_reinterpret_cast); (*p_node) = add_unbox_to_expression(*p_node, new_type, /*make_lvalue=*/FALSE); } else if (cli_or_cx_enabled && cli_string_literal_conversion_possible(old_type, new_type, (a_std_conv_descr *)NULL)&& expr_is_literal_convertible_to_cli_string(*p_node)) { /* Do a string literal conversion (string-literal --> System::String^ --> dest_type). */ check_assertion(!is_reinterpret_cast); expr = convert_expr_to_handle_to_cli_string(*p_node); check_need_for_final_cast = TRUE; *p_node = expr; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else { /* For an ordinary cast, generate the eok_cast node. */ *p_node = make_operator_node((an_expr_operator_kind)eok_cast, new_type, *p_node); (*p_node)->variant.operation.is_reinterpret_cast = is_reinterpret_cast; (*p_node)->variant.operation.is_reinterpret_like_cast = reinterpret_semantics; if (is_implicit_cast) { (*p_node)->compiler_generated = TRUE; (*p_node)->position = *err_pos; } /* if */ } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (check_need_for_final_cast) { /* Add a final cast in some cases if the primary transformation did not get us all the way to the final type. */ expr = *p_node; if (!is_implicit_cast || !cast_identical_types(new_type, expr->type)) { expr = make_operator_node((an_expr_operator_kind)eok_cast, new_type, expr); expr->compiler_generated = is_implicit_cast; *p_node = expr; } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* add_cast_to_node */ void cast_node(an_expr_node_ptr *p_node, a_type_ptr new_type, a_boolean check_cast_access, a_boolean check_ambiguity, a_boolean is_implicit_cast, a_boolean is_reinterpret_cast, a_boolean reinterpret_semantics, a_boolean within_expr_processing, a_source_position *err_pos) /* Change the type of a node. If the node is a constant, a conversion is done on the constant value; otherwise a cast operator is added on top of the node (except for implicit casts that have no effect). Check access on the cast if check_cast_access is TRUE. Check ambiguity on the cast if check_ambiguity is TRUE. If is_implicit_cast is TRUE, this is an implicit cast rather than an explicit one. Warnings about truncation etc. are issued only if is_implicit_cast is TRUE. is_reinterpret_cast is TRUE if this is a reinterpret_cast in the source; reinterpret_semantics is TRUE if the behavior is the same as a reinterpret_cast (without necessarily having that construct appear in the source). *err_pos gives the source position for errors. The current expression is assumed to be a nonconstant expression; if it were a constant expression, we wouldn't have an expression node. It's also assumed to be an evaluated expression (for purposes of error diagnosis). The caller must have already determined that the conversion is allowed, except for casts to ambiguous or inaccessible base classes. This routine does not handle user-defined conversions. This routine can be called from outside of the expression processing routines; within_expr_processing should be passed as FALSE to indicate that. */ { a_constant_ptr local_con = local_constant(); a_boolean did_not_fold; a_boolean need_cast; an_expr_node_ptr node = *p_node; a_type_ptr old_type = node->type; if (within_expr_processing && !expr_access_checking_should_be_done()) { check_cast_access = FALSE; } /* if */ /* Drop any qualifiers on the destination type, as appropriate. */ new_type = prvalue_type(new_type); /* See whether a cast operator should be added. */ if (!is_implicit_cast) { /* Explicit casts must always be represented. */ need_cast = TRUE; } else { /* An implicit cast: In some cases, such casts have no effect and need not be represented in the IL. */ if (!cast_identical_types(old_type, new_type)) { /* The cast is needed since it changes the type of the expression. */ need_cast = TRUE; } else if (is_bit_field_extract_node(node)) { /* Don't allow dropping a cast to the same type over a bit-field extraction node, because the node with the cast has different integral promotion behavior. */ need_cast = TRUE; } else { /* An implicit cast with no effect: No cast node should be created. */ need_cast = FALSE; } /* if */ } /* if */ if (!need_cast) { check_assertion(is_implicit_cast); /* We don't need to add a cast. */ } else if (m_is_error_type(new_type) || (m_is_error_type(old_type) && is_class_struct_union_type(new_type))) { /* Casting to an error type changes the node to an error node. When casting from an error type, avoid creating a cast to a class type. */ *p_node = error_node(); } else { /* Casting a node to a class type is not allowed. */ #if MICROSOFT_EXTENSIONS_ALLOWED check_assertion_str(!is_class_struct_union_type(new_type) || unboxing_conversion_possible(old_type, new_type, (a_std_conv_descr *)NULL), "cast_node: cast to class type"); #else /* !MICROSOFT_EXTENSIONS_ALLOWED */ check_assertion_str(!is_class_struct_union_type(new_type), "cast_node: cast to class type"); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ did_not_fold = TRUE; if (is_constant_node(node)) { /* Copy the constant to a local copy. Type-change the local constant and copy the result to a new constant. Since we are in a nonconstant context, reduce any error to a warning and leave the conversion to be done at runtime. */ copy_constant(node_constant(node), local_con); if (reinterpret_semantics && relaxed_constexpr_enabled && !gpp_version_is(any_version)) { did_not_fold = TRUE; } else if (within_expr_processing) { expr_type_change_constant(local_con, new_type, is_implicit_cast, check_cast_access, check_ambiguity, reinterpret_semantics, /*maintain_expression=*/FALSE, &did_not_fold, err_pos); } else { type_change_constant_full(local_con, new_type, is_implicit_cast, /*constant_context=*/FALSE, /*evaluated_context=*/TRUE, /*fold_constant_addr_exprs=*/FALSE, /*is_cli_attr_arg_expression=*/FALSE, check_cast_access, check_ambiguity, reinterpret_semantics, /*maintain_expression=*/FALSE, &did_not_fold, /*error_detected=*/(an_error_code *)NULL, err_pos); } /* if */ } /* if */ if (did_not_fold) { /* The operand is not constant. Put in a cast. */ /* Note that if the constant type-change was attempted, it was done on a copy of the constant. The original constant and expression were not changed, and therefore can be used here. */ add_cast_to_node(p_node, new_type, check_cast_access, check_ambiguity, is_implicit_cast, is_reinterpret_cast, reinterpret_semantics, err_pos); } else { /* The operation was successfully folded to a constant. */ check_assertion(is_constant_node(node)); node_constant(node) = alloc_shareable_constant(local_con); node_constant(node)->is_reinterpret_cast = is_reinterpret_cast; node->type = new_type; } /* if */ } /* if */ release_local_constant(&local_con); } /* cast_node */ void prep_generic_nontype_template_argument(an_operand *operand) /* The nontype template argument indicated by "operand" is going to be saved in the template argument list for an unknown template in a prototype instantiation. Process it so it can be turned into a constant and saved in the IL with enough information to recover whether it was an lvalue or rvalue, etc. */ { prep_generic_operand(operand); if (is_error_operand(operand)) { normalize_error_operand(operand); } else if (is_constant_operand(operand) && is_a_prvalue(operand)) { /* The operand is a constant prvalue, which we can use directly. */ } else { /* The argument is something more complicated, e.g., an expression. Make an expression and put it under a tpck_expression constant. */ make_template_param_expr_constant_operand(operand); } /* if */ } /* prep_generic_nontype_template_argument */ void prep_generic_template_argument_list(a_template_arg_ptr template_arg_list) /* The template argument list pointed to by template_arg_list is going to be saved as the template argument list for an unknown template in a prototype instantiation. Go through it and add constants for any arg_operand entries so it can go into the IL. */ { a_template_arg_ptr tap; begin_template_arg_list_traversal_simple(template_arg_list, &tap); for (; tap != NULL; advance_to_next_template_arg_simple(&tap)) { if (tap->arg_operand != NULL) { /* A template argument in arg_operand form. */ an_operand *operand = &tap->arg_operand->operand; a_constant_ptr constant; an_expr_stack_entry expr_stack_entry; a_memory_region_number region_to_switch_back_to; /* Push an expression stack entry while we do the conversion of the template argument to a constant, so we're in the right kind of context. Also, the ref entries attached to the operand get moved to the stack entry and are then recorded when the stack is popped. */ push_expr_stack((an_expression_kind)ek_template_arg, &expr_stack_entry, /*force_object_lifetime=*/FALSE, /*suppress_object_lifetime=*/FALSE); expr_stack_entry.is_template_arg_expression = TRUE; reattach_ref_entries_to_curr_expr(operand); /* Prepare the operand so we can save it without knowing the context in which it will be used. */ prep_generic_nontype_template_argument(operand); /* Fetch the constant and use it as the template argument. */ switch_to_file_scope_region(®ion_to_switch_back_to); constant = alloc_constant((a_constant_repr_kind)ck_error); extract_constant_from_operand_with_fs_fixup(operand, constant); tap->variant.constant = constant; switch_back_to_original_region(region_to_switch_back_to); #if BACK_END_IS_CP_GEN_BE if (constant->expr != NULL) { constant->expr->needed_in_cp_gen_be = TRUE; } /* if */ #endif /* BACK_END_IS_CP_GEN_BE */ if (constant->expr != NULL && curr_il_region_number != FILE_SCOPE_REGION_NUMBER && mixed_regions_in_expr_tree(constant->expr)) { /* The backing expression tree must reside entirely in one memory region. Attempt to copy it to the local region and refer to it via the local expr node reference mechanism. */ a_scope_ptr scope_for_local_ref = innermost_function_scope; an_expr_node_ptr expr = constant->expr; if (scope_for_local_ref == NULL && scope_stack_top().kind == sck_function_access) { a_scope_depth orig_depth = scope_stack_top().orig_access_depth; if (orig_depth != NO_SCOPE_DEPTH) { a_scope_depth func_depth = scope_stack[orig_depth].depth_innermost_function_scope; if (func_depth != NO_SCOPE_DEPTH) { scope_for_local_ref = scope_stack[func_depth].il_scope; } /* if */ } /* if */ } /* if */ if (scope_for_local_ref != NULL #if GENERATE_SOURCE_SEQUENCE_LISTS && !expr_stack->statement_expression_seen #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ ) { /* Refer to the underlying expression indirectly since it lives in function scope memory. (Discard the expression if a statement expression and source sequence entries are recorded, because no source sequence entries could be recorded for a template argument.) */ expr = copy_expr_tree(expr, CE_COPYING_FOR_LOCAL_EXPR_NODE_REF); constant->expr = NULL; make_local_expr_node_ref(expr, lerk_constant_expr, (char*)constant, scope_for_local_ref); } else { /* We cannot make the backing expression referenceable. */ constant->expr = NULL; } /* if */ } /* if */ free_arg_operand_list(tap->arg_operand); tap->arg_operand = NULL; pop_expr_stack(); } /* if */ } /* if */ } /* prep_generic_template_argument_list */ void make_unknown_dependent_function_operand( a_symbol_ptr sym, a_boolean is_template_id, a_template_arg_ptr template_arg_list, a_boolean is_qualified_name, an_operand *operand) /* Make a prvalue operand for the address of an unknown function from the set of overloaded functions indicated by sym. This is used in prototype instantiations when the function to be selected is not known. If the function name is followed by a list of explicit template arguments, is_template_id is TRUE and template_arg_list gives the argument list. is_qualified_name is TRUE if the source form used a qualified name. */ { a_symbol_ptr unk_sym = find_unknown_function_symbol(sym, is_qualified_name); if (!is_template_id) { /* The symbol is a constant whose value is the "address" of the unknown function. */ make_sym_constant_operand(unk_sym, operand); check_assertion(is_a_prvalue(operand)); } else { /* The function name has an explicit template argument list. Record it in a tpck_template_ref constant that points to the constant for the template (from sym). */ a_constant_ptr con = local_constant(); clear_constant(con, ck_template_param); set_template_param_constant_kind(con, tpck_template_ref); check_assertion(symbol_is(unk_sym, sk_constant) && in_file_scope(unk_sym->variant.constant)); /* Ensure any an_operand references are turned into a_constant entries. */ prep_generic_template_argument_list(template_arg_list); #if IL_SHOULD_BE_WRITTEN_TO_FILE if (record_form_of_name_reference) { /* This template argument list will be pointed to from a name reference entry. IL reading cannot deal with shared template argument lists, so make a copy. */ template_arg_list = copy_template_arg_list(template_arg_list); } /* if */ #endif /* IL_SHOULD_BE_WRITTEN_TO_FILE */ con->variant.template_param.variant.template_ref.con = unk_sym->variant.constant; con->variant.template_param.variant.template_ref.arg_list = template_arg_list; con->type = type_of_unknown_templ_param_nontype; make_constant_operand(con, operand); release_local_constant(&con); } /* if */ operand->is_qualified_name = is_qualified_name; operand->is_id_expression = TRUE; } /* make_unknown_dependent_function_operand */ void set_has_address_of_flag_if_needed(a_constant_ptr con, a_boolean flag_value) /* The caller has determined that con represents a dependent name with a preceding "address of" operator (&). Set the flag representing the presence of that operator in the underlying tpck_unknown_function entry to flag_value (unless there were errors that caused that entry not to be present). The flag might be set to FALSE after first being set to TRUE if an eok_address_of node is later explicitly added on top of a node pointing to the constant. */ { if (symbol_for(con) != NULL) { /* con may be pointing to the constant variant of an_operand. Use the associated IL entry instead. */ con = symbol_for(con)->variant.constant; } /* if */ if (constant_is(con, ck_template_param) && tpck_is(con, tpck_template_ref)) { /* A tpck_template_ref might appear on top of the tpck_unknown_function entry to specify template arguments: Skip it. */ con = con->variant.template_param.variant.template_ref.con; } /* if */ if (constant_is(con, ck_template_param) && tpck_is(con, tpck_unknown_function)) { con->variant.template_param.has_address_of = flag_value; } /* if */ } /* set_has_address_of_flag_if_needed */ void conv_indefinite_function_to_unknown_dependent_function( an_operand *operand, a_boolean force_to_rvalue) /* Convert the indicated operand (which must be an indefinite function) to a constant operand representing an unknown dependent function. The value category of the operand is preserved unless force_to_rvalue is TRUE, in which case the result is a prvalue. This is used in prototype instantiations when the function to be selected is not known. */ { a_boolean was_lvalue = is_a_function_designator(operand); an_operand orig_operand; check_assertion(is_indefinite_function_operand(operand) && is_template_dependent_context()); orig_operand = *operand; make_unknown_dependent_function_operand(operand->symbol, (a_boolean)operand->is_template_id, operand->template_arg_list, (a_boolean)operand-> is_qualified_name, operand); if (orig_operand.is_address_of_id_expression && is_constant_operand(operand)) { /* Record the presence of a unary "&" operator in the constant representation (so it can be rendered later, if needed). */ set_has_address_of_flag_if_needed(&operand->variant.constant, TRUE); } /* if */ restore_operand_details(operand, &orig_operand); restore_operand_id_details(operand, &orig_operand); if (!force_to_rvalue && was_lvalue) { change_template_param_constant_operand_to_lvalue(operand); } /* if */ restore_operand_form_of_name_reference(operand, &orig_operand); } /* conv_indefinite_function_to_unknown_dependent_function */ void cast_overloaded_function(a_type_ptr type_cast_to, an_operand *operand, a_boolean is_cast, a_boolean is_static_cast, a_boolean skip_final_adjustment) /* Cast an operand for an overloaded function (*operand) to type_cast_to. If type_cast_to is a pointer, reference, or pointer-to-member type, the cast can serve to select one of the functions in the overload set. See [over.over]. If it doesn't, an error is issued. If is_cast is TRUE, the disambiguation is being done via an explicit cast (a static_cast if is_static_cast is TRUE, otherwise an old-style or functional-notation cast); otherwise, it's implicit by context. If skip_final_adjustment is TRUE, do not do the final type adjustment to get to type_cast_to; leave the operand as the raw type based on the function selected. */ { an_arg_match_level match_level; a_symbol_ptr function_symbol, overloaded_function_symbol; a_std_conv_descr std_conversion; a_boolean ambiguous, unknown_dependent_function; a_boolean reference_case = is_reference_type(type_cast_to); a_boolean reinterpret_semantics; overloaded_function_symbol = operand->symbol; function_symbol = find_addr_of_overloaded_function_match(overloaded_function_symbol, (a_boolean)operand-> is_template_id, operand->template_arg_list, is_a_function_designator(operand), type_cast_to, is_cast, is_static_cast, &match_level, &std_conversion, &reinterpret_semantics, &unknown_dependent_function, &ambiguous); if (function_symbol != NULL) { /* The cast selects one of the overloaded functions and is valid. */ a_boolean access_error_reported; an_operand orig_operand; orig_operand = *operand; /* Do whatever would have been done with the function if we had known all along which function was intended. Make an operand for the specific function's address, a pointer-to-member for the pointer to member case. */ overloaded_function_catch_up(function_symbol, overloaded_function_symbol, &orig_operand, (a_source_position *)NULL, /*elided_reference=*/FALSE, /*compiler_generated=*/FALSE, /*result_is_lvalue=*/reference_case, /*address_taken=*/!reference_case, operand, &access_error_reported); restore_operand_details_incl_ref(operand, &orig_operand); if (!is_cast) { restore_operand_id_details(operand, &orig_operand); restore_operand_form_of_name_reference(operand, &orig_operand); } /* if */ } else if (unknown_dependent_function) { /* The cast occurs in a prototype instantiation and it is not possible to determine which function to use. */ conv_indefinite_function_to_unknown_dependent_function( operand, /*force_to_rvalue=*/!reference_case); } else { /* The cast doesn't select one of the overloaded functions, so it's an error. */ if (expr_error_should_be_issued()) { pos_sy_error(ec_indeterminate_overloaded_function, &operand->position, operand->symbol); } /* if */ conv_to_error_operand(operand); } /* if */ /* If the pointer to member is to a related class, or the pointer to function differs because of a conversion (e.g., a C++ vs. C linkage on the function type), adjust the operand. */ if (skip_final_adjustment) { /* Skip that final adjustment if told to do so. */ } else if (!reference_case) { /* This also takes care of recording the cast as part of the expression representation of the constant. */ cast_operand_full(type_cast_to, operand, (a_source_position *)NULL, /*check_cast_access=*/!(is_cast && !is_static_cast), /*check_ambiguity=*/TRUE, /*is_implicit_cast=*/!is_cast, /*is_reinterpret_cast=*/FALSE, reinterpret_semantics); } else { /* Explicit cast to reference. */ check_assertion(is_cast); cast_operand_for_reference_cast(operand, type_cast_to, /*check_cast_access=*/FALSE, /*is_implicit_cast=*/FALSE, /*reinterpret_semantics=*/FALSE); } /* if */ } /* cast_overloaded_function */ static an_expr_node_ptr remove_cast_operations(an_expr_node_ptr node) /* If node points to a sequence of eok_cast operations, return the (non-eok_cast) node pointed to by that sequence. Otherwise, return node. */ { an_expr_node_ptr snode; /* Parentheses above (but not below) a cast are skipped also. */ while ((snode = skip_parens(node), is_operation_node(snode)) && node_operator_is(snode, eok_cast)) { an_expr_node_ptr op1 = snode->variant.operation.operands; if (is_template_dependent_context() && (could_be_dependent_class_type(node->type) || could_be_dependent_class_type(op1->type))) { /* Template-dependent casts can't be discarded if they might be user-defined conversions. */ break; } /* if */ node = op1; } /* while */ return node; } /* remove_cast_operations */ an_expr_node_ptr make_node_from_void_expression_operand( an_operand_ptr operand, ARG_UNUSED a_boolean result_of_stmt_expr) /* *operand is an expression scanned as a void expression, or cast to void. Determine an expression representation for the operand, and return a pointer to the expression. result_of_stmt_expr is TRUE if operand corresponds to an expression statement that is the last statement of a GNU statement expression (and therefore that statement expression's result). */ { an_expr_node_ptr node = make_node_from_operand(operand); #if GNU_EXTENSIONS_ALLOWED if (gnu_mode && !result_of_stmt_expr) { /* In GNU mode, check for void expressions that are calls to a function marked with the "warn_unused_result" attribute. Such calls should be warned about. */ /* A cast is not treated as a "use" of a returned value in this context. */ /* See also check_expression_for_nodiscard_warning for similar processing for the "nodiscard" standard attribute (which differs in that a cast to void suppresses the warning). */ an_expr_node_ptr expr = remove_cast_operations(node); expr = skip_parens(expr); if (is_call_node(expr)) { /* Retrieve the type of the routine being called. */ a_type_ptr tp = type_of_call(expr); if (tp->kind == (a_type_kind)tk_routine && tp->variant.routine.extra_info->result_should_be_used) { expr_pos_warning(ec_call_result_should_be_used, &operand->position); } /* if */ } /* if */ } /* if */ #endif /* GNU_EXTENSIONS_ALLOWED */ return node; } /* make_node_from_void_expression_operand */ void cast_operand_to_void(an_operand *operand, a_type_ptr type_cast_to) /* Cast the indicated operand to void. This is used for explicit casts to void. type_cast_to gives the (possibly cv-qualified) void type. Lvalue-to-rvalue transformations are done on the operand if appropriate (yes in C, no in C++). Other transformations are done in all cases. */ { an_expr_node_ptr node; do_void_operand_transformations(operand, /*force_lvalue_to_rvalue=*/FALSE); /* For casts to void, we build an expression node that is a cast to void. This special cast to void is only used for the case handled here, i.e., for an explicit cast to void. cast_operand is not used because we do not wish to try to change the types of constants to void. */ node = make_node_from_void_expression_operand( operand, /*result_of_stmt_expr=*/FALSE); node = make_operator_node((an_expr_operator_kind)eok_cast, type_cast_to, node); make_expression_operand(node, operand); rule_out_expr_kinds(ROEK_INTEGRAL_CONSTANT, operand); } /* cast_operand_to_void */ void cast_operand_full(a_type_ptr new_type, an_operand *operand, a_source_position *err_pos, a_boolean check_cast_access, a_boolean check_ambiguity, a_boolean is_implicit_cast, a_boolean is_reinterpret_cast, a_boolean reinterpret_semantics) /* Cast the operand to the new type. Check access on the cast if check_cast_access is TRUE. Check ambiguity on the cast if check_ambiguity is TRUE. If is_implicit_cast is TRUE, this is an implicit cast rather than an explicit one. If there are any warnings detected on the type change, issue them only if is_implicit_cast is TRUE. Issue those diagnostics at err_pos (if err_pos is NULL, use operand's position). If is_reinterpret_cast is TRUE, this cast appeared as reinterpret_cast in the source. If reinterpret_semantics is TRUE, the operation has the same meaning as a reinterpret_cast, but it may come from another construct. The operand must be a prvalue or error operand. The caller must have already determined that the conversion is allowed, except for casts to ambiguous or inaccessible base classes. This routine does not handle user-defined conversions. */ { a_boolean did_not_fold; a_constant_ptr local_con = local_constant(); an_expr_node_ptr node; an_operand orig_operand; #if CHECKING if (!is_a_prvalue(operand) && !is_error_operand(operand)) { internal_error("cast_operand_full: operand is not a prvalue"); } /* if */ #endif /* CHECKING */ if (!expr_access_checking_should_be_done()) check_cast_access = FALSE; /* Drop any qualifiers on the destination type, as appropriate. */ new_type = prvalue_type(new_type); /* Save the operand's source position, etc. */ orig_operand = *operand; if (err_pos == NULL) err_pos = &orig_operand.position; if (m_is_error_type(new_type)) { /* Casting to an error type. Produce an error operand. */ conv_to_error_operand(operand); } else if (is_void_type(new_type)) { if (!is_void_type(operand->type)) { cast_operand_to_void(operand, new_type); } /* if */ } else { #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && operand->is_simple_string_literal && cli_string_literal_conversion_possible(operand->type, new_type, (a_std_conv_descr *)NULL) && is_literal_convertible_to_cli_string(operand, /*allow_complex=*/TRUE)) { /* Convert a string literal to a System::String^ if necessary. */ convert_operand_to_handle_to_cli_string(operand); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ switch (operand->kind) { case ok_error: /* Do nothing. */ break; case ok_expression: /* Cast the expression node. If the expression is a constant, change its type in place. Otherwise, add a cast expression node. */ /* Use the function here to get form-of-name-reference information in the expression. */ node = make_node_from_operand(operand); { #if MICROSOFT_EXTENSIONS_ALLOWED a_boolean is_special_case; a_constant_ptr con; an_expr_node_ptr con_expr; /* Microsoft mode allows some comma expressions as null pointer constants. If this is such a case, cast the underlying operand to the right pointer type. */ if (microsoft_mode && (is_pointer_type(new_type) || is_ptr_to_member_type(new_type)) && (adjust_constant_operand_info_for_microsoft_null_pointer_test( operand, &is_special_case, &con, &con_expr), is_special_case)) { an_expr_node_ptr orig_con_expr = con_expr; cast_node(&con_expr, new_type, check_cast_access, check_ambiguity, is_implicit_cast, is_reinterpret_cast, reinterpret_semantics, /*within_expr_processing=*/TRUE, err_pos); if (con_expr != orig_con_expr) { /* Replace the original node with the node incorporating the cast. Be sure to use a copy of the expression tree including the cast because we could end up with a loop in the node graph otherwise. */ overwrite_node(orig_con_expr, copy_expr_tree(con_expr, CE_NO_OPTIONS)); } /* if */ { an_expr_node_ptr tnode = node; while (tnode != orig_con_expr) { /* Reset the type on comma and parenthesis nodes from the top down to the node that's now a null pointer constant. */ check_assertion(is_operation_node(tnode)); tnode->type = new_type; if (node_operator_is(tnode, eok_parens)) { tnode = tnode->variant.operation.operands; } else { check_assertion(node_operator_is(tnode, eok_comma)); tnode = tnode->variant.operation.operands->next; } /* if */ } /* while */ } } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here. */ { cast_node(&node, new_type, check_cast_access, check_ambiguity, is_implicit_cast, is_reinterpret_cast, reinterpret_semantics, /*within_expr_processing=*/TRUE, err_pos); } /* if */ } make_expression_operand(node, operand); break; case ok_constant: /* Cast the constant by changing its type. In a nonconstant context, reduce any error to a warning and leave the conversion to be done at runtime. */ copy_constant(&operand->variant.constant, local_con); if (reinterpret_semantics && relaxed_constexpr_enabled && !gpp_version_is(any_version) && !(clang_version_is(any_version) && curr_expr_kind_is(ek_normal)) && !microsoft_mode) { did_not_fold = TRUE; } else { expr_type_change_constant( local_con, new_type, is_implicit_cast, check_cast_access, check_ambiguity, reinterpret_semantics, /*maintain_expression=*/FALSE,/*Done below*/ &did_not_fold, err_pos); } /* if */ if (did_not_fold) { /* Cast of a constant did not fold. */ if (curr_expr_kind_is_evaluated_const() && !constexpr_enabled) { error_in_operand(ec_expr_not_constant, operand); } else if (is_implicit_cast && cast_identical_types(operand->type, new_type)) { /* If the new type is identical to the old type, no cast is needed. */ } else { /* Create an expression node for the cast of the constant. */ /* Note that the constant type-change was attempted on a copy of the constant. The original constant was not changed, and therefore can be used here. */ node = make_node_from_operand(operand); add_cast_to_node(&node, new_type, check_cast_access, check_ambiguity, is_implicit_cast, is_reinterpret_cast, reinterpret_semantics, err_pos); make_expression_operand(node, operand); } /* if */ } else { /* The operation was successfully folded to a constant. */ /* Mark the constant as the result of a reinterpret_cast if it is. Don't clear the flag once it gets set (an implicit cast after a reinterpret_cast still counts as a reinterpret_cast). */ a_boolean need_backing_expr = FALSE, same_type = cast_identical_types(operand->type, new_type); if (is_reinterpret_cast) { local_con->is_reinterpret_cast = TRUE; } /* if */ /* Record the original type if it materially changed. */ if (local_con->orig_type == NULL && !same_type) { if (!prototype_instantiations_in_il && is_template_dependent_context()) { /* Don't record the original type since it may be dependent. If needed, check the backing expression instead. */ need_backing_expr = TRUE; } else { local_con->orig_type = strip_routine_default_args(operand->type); } /* if */ } /* if */ if (curr_expr_kind_is_one_in_which_const_exprs_are_recorded()) { an_expr_node_ptr orig_expr = operand->variant.constant.expr; local_con->expr = orig_expr; if (reduce_backing_expression_use && !need_backing_expr && is_implicit_cast && expr_stack->in_static_initializer && expr_stack->prev == NULL && !(orig_expr != NULL || has_name(&operand->variant.constant)) && is_arithmetic_type(new_type)) { /* A nontrivial implicit cast on top of a simple constant (i.e., one with no name and no backing expression) that is a top- level initializer expression for an entity with static storage duration. This includes values in aggregate initializers that can sometimes include many thousands of such values, such that allocating the expression tree representing each implicit cast would be prohibitive. If needed, the original type has been recorded above. (Note that in many other contexts -- e.g., in function template signatures -- this approach is not viable because the original source form must be recorded.) */ } else if (!is_implicit_cast || !same_type) { /* Record a cast expression for the constant (inhibit normal diagnostics during that process, since they were already issued). */ a_boolean saved_suppress = expr_stack->suppress_diagnostics; a_boolean saved_any_error = expr_stack->any_suppressed_error; expr_stack->suppress_diagnostics = TRUE; if (local_con->expr == NULL || (operand->variant.constant.expr != NULL && !same_type && !is_constant_node(local_con->expr) && is_arithmetic_or_unscoped_enum_type(new_type)) || /* Ignore the expression attached to a named constant. */ operand->variant.constant.is_named_constant_definition) { /* Create an expression node to which the cast history can be attached. */ /* If the original operand contains an expression and the new type is different, make sure the recorded cast expression includes the constant being cast. E.g., for (short)(10000*10000) it is useful to some consumers to know that a large value was truncated. */ local_con->expr = make_node_from_operand(operand); } /* if */ /* We're going to represent the conversion in the backing expression. local_con therefore no longer represents a "plain" named constant: Break any direct connection to such a named constant. */ break_constant_source_corresp(local_con); { a_memory_region_number region_to_switch_back_to = NULL_region_number; if (!in_file_scope(local_con->expr) && curr_il_region_number == file_scope_region_number) { /* The expression so far is in function scope memory, so put the cast there too. The constant hasn't been allocated in IL yet, so it's okay that it has a backing expression that may have a memory region problem. We'll decide about that when the allocation is done. */ switch_to_scope_region(depth_scope_stack, ®ion_to_switch_back_to); } /* if */ add_cast_to_node(&local_con->expr, new_type, /*check_cast_access=*/FALSE, /*check_ambiguity=*/FALSE, is_implicit_cast, is_reinterpret_cast, reinterpret_semantics, err_pos); switch_back_to_original_region(region_to_switch_back_to); } expr_stack->suppress_diagnostics = saved_suppress; expr_stack->any_suppressed_error = saved_any_error; } /* if */ } /* if */ eliminate_stmt_expr_refs_if_backing_expr_dropped( operand->variant.constant.expr, local_con); make_constant_operand(local_con, operand); restore_operand_form_of_name_reference(operand, &orig_operand); } /* if */ break; case ok_indefinite_function: /* Cast of overloaded function to a pointer type. Note that the legality of such casts is checked by conversion_possible. A cast cannot get here unless allowed by that routine. */ cast_overloaded_function(new_type, operand, !is_implicit_cast, /*is_static_cast=*/FALSE, /*skip_final_adjustment=*/FALSE); break; default: unexpected_condition_str("cast_operand_full: bad operand kind"); } /* switch */ } /* if */ if (vla_enabled && is_expression_operand(operand) && is_directly_variably_modified_type(new_type)) { /* Prevent the interpreter from evaluating this node, because it could fold away the side-effects of evaluating the variable bounds. */ operand->variant.expression->do_not_interpret = TRUE; } /* if */ /* Restore the original source position, etc. Keep the reference information (useful when this is a pointer to a class being cast to a base class, or a pointer to an array being cast to a pointer to the first element). */ restore_operand_details_for_cast(operand, &orig_operand, is_implicit_cast, /*incl_ref=*/TRUE); release_local_constant(&local_con); } /* cast_operand_full */ void cast_operand(a_type_ptr new_type, an_operand *operand, a_boolean is_implicit_cast) /* Simplified interface to cast_operand_full. See that routine for descriptions of the parameters. */ { cast_operand_full(new_type, operand, (a_source_position *)NULL, /*check_cast_access=*/TRUE, /*check_ambiguity=*/TRUE, is_implicit_cast, /*is_reinterpret_cast=*/FALSE, /*reinterpret_semantics=*/FALSE); } /* cast_operand */ void cast_operand_special(a_type_ptr new_type, an_operand *operand, a_source_position *err_pos, a_boolean check_cast_access, a_boolean is_implicit_cast, a_boolean is_reinterpret_cast, a_boolean reinterpret_semantics) /* Another interface to cast_operand_full. The "special" in this case is a Microsoft-bugs-mode weirdness that it's okay to cast or convert a pointer to a class to a pointer to an ambiguous base, as long as one of the ambiguous bases is a direct base class. The code here checks for that case and produces a warning instead of an error for it. This routine is called only in those contexts that allow this special laxity. */ { a_boolean check_ambiguity = TRUE; if (microsoft_bugs && !C_mode() && !reinterpret_semantics) { a_boolean baseward_cast; a_base_class_ptr bcp; if (related_class_pointers(operand->type, new_type, &baseward_cast, &bcp)&& baseward_cast && bcp->ambiguous && bcp->direct) { if (expr_diagnostic_should_be_issued( es_warning, ec_ambiguous_cast_selects_direct_base, &operand->position)) { pos_ty_warning(ec_ambiguous_cast_selects_direct_base, &operand->position, bcp->type); } /* if */ check_ambiguity = FALSE; } /* if */ } /* if */ cast_operand_full(new_type, operand, err_pos, check_cast_access, check_ambiguity, is_implicit_cast, is_reinterpret_cast, reinterpret_semantics); } /* cast_operand_special */ void conv_selector_to_object_pointer(an_operand *operand, a_boolean *is_arrow_operator) /* operand is the left operand of a selection operation (".", "->", ".*", or "->*"). *is_arrow_operator is TRUE if the operand is a prvalue address, FALSE if it's an object (lvalue or rvalue). We need to be able to deal with the operand as an address, so if *is_arrow_operator is FALSE, convert operand to an address and set *is_arrow_operator to TRUE. */ { if (!*is_arrow_operator) { /* Convert the operand to an address. */ conv_class_operand_to_object_pointer(operand); *is_arrow_operator = TRUE; operand->selector_is_object_pointer = TRUE; } /* if */ } /* conv_selector_to_object_pointer */ void base_class_cast_operand(an_operand *operand, a_base_class_ptr bcp, a_type_ptr qualifiers_model, a_boolean check_cast_access, a_boolean allow_ambiguity, a_boolean is_implicit_cast, a_boolean implicit_in_naming, a_boolean is_object_pointer) /* Cast operand to its base class identified by bcp. operand can be a prvalue pointer to class, or a class lvalue, xvalue, or prvalue. In C++/CLI mode, it can also be a handle to a class. The result will be of the same kind (pointer, lvalue, xvalue, prvalue, or handle). If qualifiers_model is non-NULL, it is a class type whose cv-qualifiers are the model for the cv-qualifiers of the result (i.e., the result's type is the base class from bcp with the cv-qualifiers from qualifiers_model). If qualifiers_model is NULL, the result will have the same cv-qualifiers as the original operand. Do access control checking on the cast if check_cast_access is TRUE. Allow an ambiguous reference if allow_ambiguity is TRUE. The cast is implicit if is_implicit_cast is TRUE. implicit_in_naming is TRUE for casts that are generated implicitly in referencing a member of a class (roughly, in getting from the name used in the source -- the projection symbol -- to the member actually used in the IL). is_object_pointer is TRUE if the pointer is asserted to be an object pointer (meaning it points at an object and is not a null pointer, though in fact the reason for this flag has to do with using 0 as a pointer in the usual version of the offsetof macro). This routine is used only in C++ mode. */ { a_boolean did_not_fold; an_expr_node_ptr node; a_constant_ptr temp_con = local_constant(); an_operand orig_operand; a_boolean class_object_case = !is_pointer_or_handle_type(operand->type); /* Save the original operand position, etc. */ orig_operand = *operand; if (!base_class_cast_access_checking_should_be_done()) { check_cast_access = FALSE; } /* if */ if (qualifiers_model == NULL) { qualifiers_model = operand->type; if (!class_object_case) { qualifiers_model = type_pointed_to(qualifiers_model); } /* if */ } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled) { a_type_ptr type = operand->type; if (!class_object_case) type = type_pointed_to(type); if (is_value_class_type(type)) { /* When you cast from a C++/CLI value class type to a base class, the original operand gets boxed, because the base class is a ref class and can only be accessed in a managed heap object. ECMA 34.5.1 suggests that this happens only when calling a member function of the base class, but putting it here at a low level ensures that you never get a ref class object in the wrong place. */ an_expr_node_ptr expr; /* Convert the operand to a prvalue if necessary. */ do_operand_transformations(operand, TOPT_NO_OPTIONS); expr = make_node_from_operand(operand); if (!class_object_case) { /* For the pointer case, get the underlying class object so we can box it. */ expr = add_indirection_to_node(expr); expr = rvalue_expr_for_lvalue(expr); } /* if */ expr = add_box_to_expression(expr, /*is_implicit=*/TRUE, /*handle_to_form=*/FALSE); if (class_object_case) { expr = add_indirection_to_node(expr); } /* if */ make_lvalue_or_rvalue_expression_operand(expr, operand); } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (is_error_operand(operand)) { normalize_error_operand(operand); } else { did_not_fold = TRUE; if (curr_expr_is_evaluated() && expr_stack->favor_constant_result && is_constant_operand(operand) && operand->variant.constant.kind != (a_constant_repr_kind)ck_aggregate) { /* Fold a cast of a constant address into another constant address. This folding is always done in constant expressions, but in some nonconstant expressions it's not done because it's clearer to have the cast in the IL (the constant form has only an offset, and loses the sequence of casts). */ an_error_code error_detected = ec_no_error; an_error_code *p_error_detected = NULL; if (expr_stack->suppress_diagnostics) p_error_detected = &error_detected; fold_base_class_cast(&operand->variant.constant, bcp, qualifiers_model, temp_con, check_cast_access, /*check_ambiguity=*/TRUE, is_implicit_cast, is_object_pointer, /*omit_back_expr=*/FALSE, &did_not_fold, &orig_operand.position, p_error_detected); if (error_detected != ec_no_error) record_suppressed_error(); } /* if */ if (did_not_fold) { /* The cast could not be folded to a constant. */ if (curr_expr_kind_is_traditional_const() && curr_expr_is_evaluated() && !(is_an_lvalue(operand) && (curr_expr_kind_is(ek_template_arg) || curr_expr_kind_is(ek_init_constant)))) { /* The cast must fold to a constant in a constant expression. Certain expression kinds allow an lvalue because its address might be taken later. */ error_in_operand(ec_expr_not_constant, operand); } else { /* Build an expression node or nodes for the cast. */ node = make_node_from_operand(operand); add_base_class_casts(bcp, qualifiers_model, check_cast_access, /*check_ambiguity=*/TRUE, allow_ambiguity, is_implicit_cast, implicit_in_naming, &node, &orig_operand.position, (a_boolean *)NULL); make_lvalue_or_rvalue_expression_operand(node, operand); } /* if */ } else { /* The cast was folded to a constant. */ make_constant_operand(temp_con, operand); } /* if */ } /* if */ /* Restore the original source position, etc. */ restore_operand_details_for_cast(operand, &orig_operand, is_implicit_cast, /*incl_ref=*/TRUE); release_local_constant(&temp_con); } /* base_class_cast_operand */ void adjust_glvalue_type(an_operand *operand, a_type_ptr dest_type) /* *operand is a glvalue (not necessarily of class type). Adjust its type, if necessary, to dest_type, which may differ from the current type in being a base class or having different cv-qualifiers. This adjustment does not make a new object; it merely adjusts the operand to access the same object with a new type. On return, the operand is still an lvalue or xvalue as it was on entry. This adjustment is implicit (e.g., in binding a reference) and not something explicit like a cast. */ { if (is_error_operand(operand)) { normalize_error_operand(operand); } else { a_type_ptr operand_type = operand->type; check_assertion(is_a_glvalue(operand)); if (!cast_identical_types(operand_type, dest_type)) { /* If you change this, see expr_before_type_adjustment. */ a_base_class_ptr bcp = NULL; an_operand orig_operand; orig_operand = *operand; if (is_class_struct_union_type(operand_type) && is_class_struct_union_type(dest_type)) { a_type_ptr source_class_type = skip_typerefs(operand_type); a_type_ptr dest_class_type = skip_typerefs(dest_type); /* Look for a required base-class adjustment. */ if (!same_entities(source_class_type, dest_class_type)) { bcp = find_base_class_of(source_class_type, dest_class_type); } /* if */ } /* if */ if (bcp != NULL) { /* Cast to a base class, and also adjust cv-qualifiers if necessary. */ base_class_cast_operand(operand, bcp, dest_type, /*check_cast_access=*/TRUE, /*allow_ambiguity=*/FALSE, /*is_implicit_cast=*/TRUE, /*implicit_in_naming=*/FALSE, /*is_object_pointer=*/FALSE); } else { /* Adjust cv-qualifiers or the underlying type. */ an_expr_node_ptr expr = make_node_from_operand(operand); expr = add_cast_to_glvalue(expr, dest_type); make_glvalue_expression_operand(expr, operand); } /* if */ restore_operand_details_incl_ref(operand, &orig_operand); } /* if */ } /* if */ } /* adjust_glvalue_type */ void conv_rvalue_reference_result_to_xvalue(an_operand *operand) /* The indicated operand is the result of an rvalue reference operation (e.g., a cast to an rvalue reference type or a call of a function returning an rvalue reference type). It's been created as an lvalue; convert it to an xvalue instead. */ { /* Note that function designators are not changed -- a cast to an rvalue reference type to function produces an lvalue. */ if (is_an_lvalue(operand)) { /* Convert an lvalue to an xvalue. */ an_expr_node_ptr expr; check_assertion(is_expression_operand(operand)); expr = operand->variant.expression; check_assertion(expr->is_lvalue); expr->is_lvalue = FALSE; expr->is_xvalue = TRUE; } /* if */ } /* conv_rvalue_reference_result_to_xvalue */ void conv_reference_cast_operand_to_lvalue_if_necessary(an_operand *operand, a_type_ptr dest_type) /* The indicated operand is the source of a cast to a reference type. If it is a prvalue, convert it to an lvalue. This is necessary because the IL operators for reference casts take a glvalue as their operand. dest_type gives the destination reference type. */ { an_expr_node_ptr temp_init_node; check_assertion(dest_type != NULL && is_any_reference_type(dest_type)); if (is_a_prvalue(operand)) { if (is_class_struct_union_type(operand->type)) { conv_class_prvalue_operand_to_lvalue(operand); } else if (is_array_type(operand->type) && operand_is_temp_init_full(operand, &temp_init_node)) { /* Convert an array prvalue temp to an lvalue temp. */ a_boolean success; an_operand orig_operand; orig_operand = *operand; temp_init_node = conv_prvalue_expr_to_lvalue(temp_init_node, &success, /*see_if_possible=*/FALSE, /*gcc_lvalue=*/FALSE, /*ignore_casts=*/FALSE, (a_type_ptr *)NULL); check_assertion(success); make_glvalue_expression_operand(temp_init_node, operand); restore_operand_details(operand, &orig_operand); } else { a_type_ptr under_type = type_pointed_to(dest_type); a_type_ptr req_type = NULL; /* Pick req_type so that we get a const-qualified temporary if we're casting to a reference to const. */ if (identical_types_ignoring_qualifiers(operand->type, under_type)) { req_type = under_type; } else if (!is_error_type(under_type) && !is_error_operand(operand)) { req_type = type_plus_qualifiers_from_second_type(operand->type, under_type); } /* if */ temp_init_from_operand_full(operand, req_type, /*result_is_lvalue=*/TRUE); } /* if */ } /* if */ } /* conv_reference_cast_operand_to_lvalue_if_necessary */ void mark_as_reference_cast(an_expr_node_ptr expr, a_type_ptr ref_type) /* expr represents a cast to the reference type ref_type. Set flags in the expression node to indicate that. */ { check_assertion(is_cast_operation_node(expr) && is_any_reference_type(ref_type)); expr->variant.operation.is_reference_cast = TRUE; expr->orig_lvalue_type = type_pointed_to(ref_type); if (is_rvalue_reference_type(ref_type)) { expr->variant.operation.is_rvalue_reference_cast = TRUE; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (is_tracking_reference_type(ref_type)) { expr->variant.operation.is_tracking_reference_cast = TRUE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ } /* mark_as_reference_cast */ void cast_operand_for_reference_cast(an_operand *operand, a_type_ptr dest_type, a_boolean check_cast_access, a_boolean is_implicit_cast, a_boolean reinterpret_semantics) /* Cast *operand to a reference type given by dest_type. That produces an lvalue of the type underlying dest_type as the result, or an xvalue if dest_type is an rvalue reference. This cast does not make a new object; it merely adjusts the operand to access the same object with a new type. Check access on related-class casts if check_cast_access is TRUE. The cast is implicit if is_implicit_cast is TRUE. Consider related-class adjustments only if reinterpret_semantics is FALSE (it is TRUE for reinterpret_cast). The input operand is generally an lvalue, but it can be an rvalue when dest_type is an rvalue reference or when dest_type is an lvalue reference to const. */ { a_type_ptr underlying_type; a_boolean is_rvalue_ref = FALSE; check_assertion(is_any_reference_type(dest_type)); is_rvalue_ref = is_rvalue_reference_type(dest_type); underlying_type = type_pointed_to(dest_type); if (is_rvalue_ref) { if (!is_an_rvalue(operand) && binding_rvalue_ref_to_bit_field_allowed() && is_bit_field_operand(operand)) { /* MSVC10 allows casting a bit field to an rvalue reference by converting it to an rvalue. */ conv_glvalue_to_prvalue(operand); } /* if */ } /* if */ if (is_a_prvalue(operand)) { /* If the caller passes in a prvalue, convert it to an lvalue. */ conv_reference_cast_operand_to_lvalue_if_necessary(operand, dest_type); } else { (void)check_for_taking_the_address_of_a_bit_field(operand, &operand->position); #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled) { /* Check for binding a reference to a C++/CLI initonly field. */ (void)check_for_address_of_or_reference_to_initonly_field( operand, &operand->position, /*reference_case=*/TRUE); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ if (is_error_operand(operand)) { normalize_error_operand(operand); } else if (is_error_type(underlying_type)) { conv_to_error_operand(operand); } else { a_type_ptr operand_type = operand->type; a_base_class_ptr bcp = NULL; a_boolean derived_class_cast = FALSE; a_boolean need_eok_ref_cast = FALSE; an_operand orig_operand; orig_operand = *operand; check_assertion(is_a_glvalue(operand) || is_a_function_designator(operand)); if (!reinterpret_semantics && is_class_struct_union_type(operand_type) && is_class_struct_union_type(underlying_type)) { a_type_ptr source_class_type = skip_typerefs(operand_type); a_type_ptr dest_class_type = skip_typerefs(underlying_type); /* Look for a required base-class adjustment. */ if (!same_entities(source_class_type, dest_class_type)) { bcp = find_base_class_of(source_class_type, dest_class_type); if (bcp == NULL) { bcp = find_base_class_of(dest_class_type, source_class_type); if (bcp != NULL) derived_class_cast = TRUE; } /* if */ } /* if */ } /* if */ if (bcp == NULL) { need_eok_ref_cast = TRUE; } else { if (!derived_class_cast) { /* Cast to a base class, and also adjust cv-qualifiers if necessary. */ base_class_cast_operand(operand, bcp, underlying_type, check_cast_access, /*allow_ambiguity=*/FALSE, is_implicit_cast, /*implicit_in_naming=*/FALSE, /*is_object_pointer=*/FALSE); } else { /* Cast to a derived class, and also adjust cv-qualifiers if necessary. Note that access checking is never done on derived-class casts; the casts that can specify them also suppress access checking. */ an_expr_node_ptr expr = make_node_from_operand(operand); check_assertion(!is_implicit_cast); add_derived_class_casts(underlying_type, bcp, /*check_ambiguity=*/TRUE, /*requires_runtime_check=*/FALSE, &expr, &orig_operand.position, (a_boolean *)NULL); make_glvalue_expression_operand(expr, operand); } /* if */ if (is_rvalue_ref) { /* The result of a cast to an rvalue reference type is an xvalue. We need an eok_ref_cast after the related-class conversion to effectuate the conversion to xvalue. */ need_eok_ref_cast = TRUE; if (!is_implicit_cast) { /* The base/derived cast processing marked all the steps except the last as implicit steps. Since we're adding one more node on top, mark the last-so-far as an implicit step too. */ an_expr_node_ptr expr = operand->variant.expression; expr->variant.operation.implicit_step_of_explicit_cast = TRUE; } /* if */ } else if (is_expression_operand(operand)) { /* We don't need the final eok_ref_cast, so mark the top node of the related-class cast as part of a reference cast. */ an_expr_node_ptr expr = operand->variant.expression; mark_as_reference_cast(expr, dest_type); } /* if */ } /* if */ if (need_eok_ref_cast) { /* Do any non-base-class type adjustment. */ an_expr_node_ptr expr = make_node_from_operand(operand); expr = make_lvalue_operator_node((an_expr_operator_kind)eok_ref_cast, underlying_type, expr); mark_as_reference_cast(expr, dest_type); if (is_implicit_cast) expr->compiler_generated = TRUE; make_glvalue_expression_operand(expr, operand); if (is_rvalue_ref) { /* The result of a cast to an rvalue reference type is an xvalue. */ conv_rvalue_reference_result_to_xvalue(operand); } /* if */ } /* if */ restore_operand_details_for_cast(operand, &orig_operand, is_implicit_cast, /*incl_ref=*/is_a_glvalue(operand)); } /* if */ } /* cast_operand_for_reference_cast */ void adjust_class_prvalue_type(an_operand *operand, a_type_ptr dest_type) /* *operand is a class prvalue. Adjust its type, if necessary, to dest_type, which may differ from the current type in having different cv-qualifiers. This adjustment does not make a new object; it merely adjusts the operand to access the same object with a new type. On return, the operand is still a prvalue. This adjustment is implicit (e.g., in binding a reference) and not something explicit like a cast. */ { if (is_error_operand(operand)) { normalize_error_operand(operand); } else if (is_error_type(dest_type)) { conv_to_error_operand(operand); } else { a_type_ptr operand_type = operand->type; if (!cast_identical_types(operand_type, dest_type)) { if (is_constant_operand(operand)) { operand->variant.constant.type = operand->type = dest_type; } else { /* If you change this, see expr_before_type_adjustment. */ an_expr_node_ptr node; an_operand orig_operand; orig_operand = *operand; check_assertion(is_a_prvalue(operand) && is_class_struct_union_type(operand_type) && types_are_compatible_ignoring_qualifiers(operand_type, dest_type)); node = make_node_from_operand(operand); node = add_rvalue_class_adjust_node(node, dest_type); make_expression_operand(node, operand); restore_operand_details(operand, &orig_operand); } /* if */ } /* if */ } /* if */ } /* adjust_class_prvalue_type */ a_boolean is_a_cplusplus_lvalue(an_operand *operand) /* Return TRUE if the indicated operand is a C++ lvalue. The C++ standard definition of lvalue includes what are called function designators in C. Note this is "lvalue", not "glvalue". */ { a_boolean is_lvalue = is_an_lvalue(operand) || (is_a_function_designator(operand) && !is_sym_for_member_operand(operand) && !is_indefinite_function_operand(operand)); return is_lvalue; } /* is_a_cplusplus_lvalue */ static a_type_ptr type_after_bit_field_integral_promotion(an_expr_node_ptr node) /* node is a bit-field selection operation (glvalue or prvalue). Determine the type that would result from applying the integral promotions to the selection type. Return the promoted type, which may be the same as the original type. The node is not actually promoted; it is up to the caller to do the cast if desired. */ { a_type_ptr type, promoted_type; a_field_ptr field; an_integer_kind ikind, orig_ikind; unsigned int field_size; an_expr_node_ptr field_node; db_enter(4, "type_after_bit_field_integral_promotion"); check_assertion(is_operation_node(node)); field_node = node->variant.operation.operands->next; check_assertion(field_node != NULL && field_node->kind == (an_expr_node_kind)enk_field); field = node_field(field_node); field_size = field->bit_size; type = node->type; if (!is_glvalue_node(node)) type = prvalue_type(type); promoted_type = skip_typerefs(type); if (is_template_param_type(promoted_type) || is_scoped_enum_type(promoted_type) || is_error_type(promoted_type)) { goto end_of_routine; } /* if */ /* The type of a bit-field should be integral. */ check_assertion_str( type_is(promoted_type, tk_integer), "type_after_bit_field_integral_promotion: bit-field not integral"); orig_ikind = ikind = promoted_type->variant.integer.int_kind; if (is_bit_precise_kind(ikind)) { /* Bit-precise integer types are not subject to integral promotion. */ goto end_of_routine; } /* if */ check_assertion_str( field_size <= (unsigned int)(targ_sizeof_largest_integer*targ_char_bit), "type_after_bit_field_integral_promotion: bit-field too big"); if (!C_mode()) { /* In C++, the size of a bit field can be larger than the size of the underlying type. The value representation only includes the number of bits in the underlying type, however. Base the promotion rules on that. See [class.bit]p1 in the C++ standard. */ a_targ_size_t int_size; a_targ_alignment int_alignment; get_integer_size_and_alignment(ikind, &int_size, &int_alignment); int_size *= targ_char_bit; if (field_size > int_size) field_size = (unsigned int)int_size; } /* if */ if (is_immediate_enum_type(promoted_type) && ikind >= (an_integer_kind)ik_int) { /* Enum bit fields keep their underlying type when being promoted, unless this underlying type is smaller than an int. */ } else if (gcc_version_is(< 40000) && field->bit_size == (unsigned int)(targ_sizeof_long*targ_char_bit)) { /* gcc before 4.0 considers bit fields that are exactly as long as "long" to retain their types. */ } else if ((microsoft_mode || gpp_version_is(< 40200)) && ikind >= (an_integer_kind)ik_int) { /* MSVC++ and g++ before 4.2.0 consider int and larger bit fields to retain their types even if the bit field size is less than the size of int. */ } else if (field->bit_field_is_signed) { /* Bit-field is signed, so it is promoted to int if all its values will fit in int. */ if (field_size <= (unsigned int)(targ_sizeof_int*targ_char_bit)) { ikind = (an_integer_kind)ik_int; } else { /* Bit-fields larger than int keep the original type. */ } /* if */ } else { /* Bit-field is unsigned, so it is promoted to the first of int or unsigned int into which all its values will fit. */ if (clang_mode && field->declared_bit_size == (unsigned int)(targ_sizeof_int*targ_char_bit)) { /* Clang promotes over-sized unsigned bitfields with the width of an int to be unsigned ints, not ints. E.g., "unsigned char c:32" promotes to unsigned int. */ ikind = (an_integer_kind)ik_unsigned_int; } else if (field_size < (unsigned int)(targ_sizeof_int*targ_char_bit)) { ikind = (an_integer_kind)ik_int; } else if (field_size == (unsigned int)(targ_sizeof_int*targ_char_bit)) { ikind = (an_integer_kind)ik_unsigned_int; #if LONG_LONG_ALLOWED } else if (gcc_mode && gnu_version == 40000 && ikind == (an_integer_kind)ik_unsigned_long_long && field_size < (unsigned int)(targ_sizeof_long_long*targ_char_bit)) { /* gcc 4.0 promotes unsigned long long bit fields of less than 64 bits to signed long long. This doesn't seem to conform to the C99 standard (6.3.1.1p2). */ ikind = (an_integer_kind)ik_long_long; #endif /* LONG_LONG_ALLOWED */ } else { /* Bit-fields larger than unsigned int keep the original type. */ } /* if */ } /* if */ if (ikind != orig_ikind || is_immediate_enum_type(promoted_type)) { /* If the integer kind changed, update the promoted type to the promoted kind. Even if it didn't change, return the underlying type in the case of an enum type (unscoped; scoped enum types don't get here). */ promoted_type = integer_type(ikind); } /* if */ end_of_routine: db_exit(); return promoted_type; } /* type_after_bit_field_integral_promotion */ static a_boolean requires_bit_field_promotion(an_expr_node_ptr node, a_type_ptr *p_type) /* A bit-field expression is a direct access to a bit field, or such an access under operations that maintain the bit-field nature of the access. The exact meaning of this is unfortunately a little unclear in both the C and C++ standards. Still, if p->bf is a direct access to a bit field that is smaller than an int, the usual interpretation is that expressions like ++p->bf, (x, p->bf), and p->bf = 1 are still bit-field expressions and that if the accessed bit field is smaller than an int, the promoted type of the expression is int (even if the bit field itself has, e.g., unsigned int type). This function returns TRUE if the given node is a bit-field expression (except in modes where bit-field expressions have no special treatment), and in that case it sets *p_type to the bit-field's promoted type. */ { a_boolean result = TRUE; a_field_ptr fp = NULL; if (microsoft_mode || C_dialect == C_dialect_pcc) { /* pcc and MSVC don't treat bit fields in a special way. */ result = FALSE; goto loop_done; } else if (!bit_field_promotion_applies_to_some_operations) { /* Just check the case of a direct access to a bit field. */ if (is_bit_field_extract_node(node)) { if (is_variable_node(node)) { a_variable_ptr vp = node_variable(node); check_assertion(vp->init_kind == (an_init_kind)initk_binding); node = vp->initializer.bound_expr; } *p_type = type_after_bit_field_integral_promotion(node); } else { result = FALSE; } /* if */ goto loop_done; } /* if */ for (;;) { if (is_operation_node(node)) { switch (node->variant.operation.kind) { case eok_dot_field: case eok_points_to_field: fp = node_field(node->variant.operation.operands->next); if (fp->is_bit_field) { /* The given expression was a bit-field expression. Determine its promoted type. */ *p_type = type_after_bit_field_integral_promotion(node); } else { result = FALSE; } /* if */ goto loop_done; case eok_parens: 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: /* Operations that preserve the bit-field nature of their first operand. */ node = node->variant.operation.operands; break; case eok_pre_incr: case eok_pre_decr: if (clang_mode) { /* Clang doesn't promote the prefix increment/decrement operations. */ result = FALSE; goto loop_done; } /* if */ node = node->variant.operation.operands; break; case eok_post_decr: case eok_post_incr: if ((!C_mode() && !gpp_mode) || clang_mode) { /* C++ doesn't make a postfix increment/decrement a bit field even if the operand is one, but GCC does. (Clang doesn't in any mode.) */ result = FALSE; goto loop_done; } /* if */ node = node->variant.operation.operands; break; case eok_comma: /* The comma operator preserves the bit-field nature of its second operand. */ node = node->variant.operation.operands->next; break; default: result = FALSE; goto loop_done; } /* switch */ } else if (is_variable_node(node)) { a_variable_ptr vp = node_variable(node); if (vp->init_kind != (an_init_kind)initk_binding) { result = FALSE; break; } /* if */ node = vp->initializer.bound_expr; } else { result = FALSE; break; } /* if */ } /* for */ loop_done: return result; } /* requires_bit_field_promotion */ #if DO_IL_LOWERING a_type_ptr node_type_after_integral_promotion(an_expr_node_ptr node) /* Determine the type that would result from applying the integral promotions to the indicated expression. Return the promoted type, which may be the same as the original type. The expression is an rvalue. */ { a_type_ptr promoted_type; /* Check for bit-field accesses, which require special handling. The special processing is not done in pcc and Microsoft modes. */ if (!requires_bit_field_promotion(node, &promoted_type)) { promoted_type = type_after_integral_promotion(node->type); } /* if */ return promoted_type; } /* node_type_after_integral_promotion */ #endif /* DO_IL_LOWERING */ a_type_ptr operand_type_after_integral_promotion(an_operand *operand) /* Determine the type that would result from applying the integral promotions (3.2.1.1) to *operand. Return the promoted type, which may be the same as the original type. The operand may be a prvalue or a glvalue; if it is a glvalue, the type returned is the promoted version of the prvalue type for that glvalue (i.e., some cv-qualifiers are dropped even if the type is not integral). */ { a_type_ptr promoted_type = NULL; an_expr_node_ptr expr = expr_node_from_operand(operand); /* Check for bit-field accesses, which may require special handling. */ if (!(expr != NULL && requires_bit_field_promotion(expr, &promoted_type))) { /* Non-bit-field cases. Determine the promoted type on the basis of the operand type. */ promoted_type = operand->type; if (is_a_glvalue(operand)) { /* For a glvalue, use the type it would have if it were a prvalue. */ promoted_type = prvalue_type(promoted_type); } /* if */ promoted_type = type_after_integral_promotion(promoted_type); } /* if */ return promoted_type; } /* operand_type_after_integral_promotion */ void promote_operand(an_operand *operand) /* Determine the integral promotion and do the promotion on an operand. See 3.2.1.1 in the standard. The operand must be a prvalue. */ { cast_operand(operand_type_after_integral_promotion(operand), operand, /*is_implicit_cast=*/TRUE); } /* promote_operand */ static void handle_nonstandard_array_rvalue(an_operand *operand) /* If operand is an array rvalue, convert it to a pointer to the first element of the array. This is used for the nonstandard conversion in pre-C99 C. A diagnostic is issued in strict mode. */ { if (is_an_rvalue(operand) && is_array_type(operand->type)) { /* For C++ and C99 mode, this should have been done earlier. */ check_assertion(C_mode() && !c99_mode); if (strict_ansi_mode) { expr_pos_diagnostic(strict_ansi_discretionary_severity, ec_bad_rvalue_array, &operand->position); } /* if */ /* Convert the array rvalue to a pointer to the first element. */ do_array_to_pointer_conversion(operand); } /* if */ } /* handle_nonstandard_array_rvalue */ void arg_default_promote_operand(an_operand *argument_operand, a_boolean is_ellipsis) /* Do default argument promotions on an argument operand. If the operand is a glvalue, it is converted to a prvalue before doing the promotions. is_ellipsis if the argument is being passed to an ellipsis. If it's FALSE, we just have a case where we have no parameter information at all, which happens, for example, for an unprototyped function in C mode. */ { a_type_ptr orig_arg_type, arg_type; if (microsoft_bugs && is_ellipsis && !curr_expr_is_evaluated() && is_an_lvalue(argument_operand) && is_incomplete_type(argument_operand->type) && is_class_struct_union_type(argument_operand->type)) { /* MSVC++ (7.1 at least) allows an lvalue of an incomplete class type as an argument to an ellipsis in a call in an unevaluated context. Note that MSVC++ does not instantiate a template class in that case so we don't call complete_type_is_needed here. */ } else { /* Convert the operand to a prvalue if necessary. */ do_operand_transformations(argument_operand, TOPT_NO_OPTIONS); /* Catch array rvalues in pre-C99 C. In strict mode they are not allowed. Otherwise, do the special array --> pointer decay as an extension. */ handle_nonstandard_array_rvalue(argument_operand); } /* if */ orig_arg_type = argument_operand->type; arg_type = skip_typerefs(orig_arg_type); /* Do the integral promotions part of the default argument promotions directly on the operand because of the special case with bit-fields (which can't be handled from just the type). */ if (arg_type->kind == (a_type_kind)tk_integer) { if (arg_type->variant.integer.is_scoped_enum) { /* Scoped enum types are not usually subject to integral promotions, but if we pass them through an ellipsis parameter, we do convert them to the promoted underlying type. */ if (is_ellipsis) { a_type_ptr promoted_type; promoted_type = type_after_integral_promotion( integer_type(arg_type->variant.integer.int_kind)); cast_operand(promoted_type, argument_operand, /*is_implicit_cast=*/TRUE); } /* if */ } else { promote_operand(argument_operand); } /* if */ } else if (arg_type->incomplete) { /* Catch a case like "f((void)2)" -- an argument with an incomplete type is not allowed. */ if (microsoft_bugs && !curr_expr_is_evaluated() && is_ellipsis && is_immediate_class_type(arg_type)) { /* MSVC++ allows a call returning an incomplete class type as an argument for an ellipsis in an unevaluated context. */ } else { expr_issue_incomplete_type_diag(&argument_operand->position, arg_type); conv_to_error_operand(argument_operand); } /* if */ } else if (is_immediate_class_type(arg_type)) { /* Class. No promotion needed. */ if (!C_mode()) { if (is_ellipsis && is_nonPOD_or_has_nontrivial_copy_semantics(arg_type) && curr_expr_is_evaluated()) { /* Warn on passing a non-POD class (a class with non-trivial copy semantics, in modern C++) to an ellipsis. */ expr_pos_warning(ec_non_pod_passed_to_ellipsis, &argument_operand->position); } /* if */ #if USE_CCTOR_TO_PASS_CLASS_TO_ELLIPSIS /* If configured that way, call the copy constructor for a class object, and pass the address of the temporary into which the copy was placed. This falls under undefined behavior. The Sun CC compiler uses the copy constructor in this case. Note that if the class does not have a copy constructor this does the right thing anyway. */ prep_arg_passed_via_copy_constructor(argument_operand, orig_arg_type, (a_conv_descr *)NULL, ec_no_suitable_copy_constructor); #endif /* USE_CCTOR_TO_PASS_CLASS_TO_ELLIPSIS */ } /* if */ } else { cast_operand(default_argument_promotion(orig_arg_type), argument_operand, /*is_implicit_cast=*/TRUE); } /* if */ } /* arg_default_promote_operand */ void build_unary_result_operand(an_operand *operand, an_expr_operator_kind kind, a_type_ptr type, an_operand *result) /* Build an operand for the expression that is the operator "kind" operating on "operand", with result type "type". The operand is a prvalue. */ { an_expr_node_ptr node; node = make_node_from_operand(operand); node = make_operator_node(kind, type, node); make_expression_operand(node, result); copy_operand_position(operand, result); } /* build_unary_result_operand */ void build_binary_result_operand_full(an_operand *operand_1, an_operand *operand_2, an_expr_operator_kind kind, a_type_ptr type, a_boolean result_is_lvalue, an_operand *result) /* Build an operand for the expression that is the operator "kind" operating on "operand_1" and "operand_2", with result type "type". The result is an lvalue if result_is_lvalue is TRUE. */ { an_expr_node_ptr node; if (kind == (an_expr_operator_kind)eok_error) { /* If the operator is an error, the expression tree cannot be built; return an error operand. eok_error is returned by which_binary_operator to indicate a case where the operator cannot be determined. */ make_error_operand(result); } else { /* Make nodes from the operands, and link the first node to the second. */ if (kind == (an_expr_operator_kind)eok_comma) { node = make_node_from_void_expression_operand( operand_1, /*result_of_stmt_expr=*/FALSE); } else { node = make_node_from_operand(operand_1); } /* if */ node->next = make_node_from_operand(operand_2); /* Make an expression operator node which has the above operand nodes. */ node = make_operator_node(kind, type, node); if (strict_cpp17_eval_order) { a_boolean eval_left_to_right, eval_right_to_left; eval_order_for_binary_node_kind(kind, &eval_left_to_right, &eval_right_to_left); node->variant.operation.eval_right_to_left = eval_right_to_left; node->variant.operation.eval_left_to_right = eval_left_to_right; } /* if */ /* Make an operand of the expression. */ make_expression_operand(node, result); if (result_is_lvalue) { node->is_lvalue = TRUE; if (kind == (an_expr_operator_kind)eok_comma) { node->variant.operation.returns_lvalue_instead_of_usual_rvalue = TRUE; } /* if */ set_glvalue_operand_state(result); } /* if */ } /* if */ copy_operand_position(operand_1, result); #if EXTRA_SOURCE_POSITIONS_IN_IL result->end_position = operand_2->end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* build_binary_result_operand_full */ void build_binary_result_operand(an_operand *operand_1, an_operand *operand_2, an_expr_operator_kind kind, a_type_ptr type, an_operand *result) /* Build an operand for the expression that is the operator "kind" operating on "operand_1" and "operand_2", with result type "type". The result is a prvalue. */ { build_binary_result_operand_full(operand_1, operand_2, kind, type, /*result_is_lvalue=*/FALSE, result); } /* build_binary_result_operand */ void build_question_result_operand( an_operand *operand_1, an_operand *operand_2, an_operand *operand_3, a_type_ptr result_type, a_boolean result_is_an_lvalue, ARG_UNUSED a_boolean is_gnu_two_operand_form, an_operand *result) /* Build an operand for the expression that is the operator "?" operating on operand_1, operand_2, and operand_3, with result type result_type, and an lvalue if result_is_an_lvalue is TRUE. is_gnu_two_operand_form is TRUE if this is a GNU two-operand "?" (a synthesized operand_2 is still provided). */ { an_expr_node_ptr expr; an_expr_operator_kind op = (an_expr_operator_kind)eok_question; expr = make_node_from_operand(operand_1); #if GNU_VECTOR_TYPES_ALLOWED if (is_vector_type(operand_1->type)) { op = (an_expr_operator_kind)eok_vector_question; } /* if */ #endif /* GNU_VECTOR_TYPES_ALLOWED */ expr = make_operator_node(op, result_type, expr); #if GNU_EXTENSIONS_ALLOWED expr->variant.operation.is_gnu_two_operand_question_mark = is_gnu_two_operand_form; #endif /* GNU_EXTENSIONS_ALLOWED */ expr->variant.operation.operands->next = make_node_from_operand(operand_2); expr->variant.operation.operands->next->next = make_node_from_operand(operand_3); if (result_is_an_lvalue) { expr->is_lvalue = TRUE; expr->variant.operation.returns_lvalue_instead_of_usual_rvalue = TRUE; } /* if */ make_lvalue_or_rvalue_expression_operand(expr, result); } /* build_question_result_operand */ /* Type predicates used by determine_arithmetic_conversions_full. */ #if INT128_EXTENSIONS_ALLOWED #define is_unsigned_int128(ikind) \ ((ikind) == (an_integer_kind)ik_unsigned_int128) #define is_int128(ikind) \ ((ikind) == (an_integer_kind)ik_int128) #endif /* INT128_EXTENSIONS_ALLOWED */ #if LONG_LONG_ALLOWED #define is_unsigned_long_long(ikind) \ ((ikind) == (an_integer_kind)ik_unsigned_long_long) #define is_long_long(ikind) \ ((ikind) == (an_integer_kind)ik_long_long) #endif /* LONG_LONG_ALLOWED */ #define is_unsigned_long(ikind) \ ((ikind) == (an_integer_kind)ik_unsigned_long) #define is_long(ikind) \ ((ikind) == (an_integer_kind)ik_long) #define is_unsigned_int(ikind) \ ((ikind) == (an_integer_kind)ik_unsigned_int) static a_float_kind promoted_float_kind(a_float_kind fkind_1, a_float_kind fkind_2) /* fkind_1 and fkind_2 represent the precision of a floating-point type involved in a binary operation (or fk_last if the corresponding operand does not have a floating-point type). Return the precision of the result (i.e., the precision to which both operands should be promoted). If both fkind_1 and fkind_2 are fk_last, then fk_last is returned. */ { a_float_kind result; a_float_kind other; a_targ_size_t m_bits_1 = num_mantissa_bits[(int)fkind_1], m_bits_2 = num_mantissa_bits[(int)fkind_2]; /* The promoted kind is the one with the highest precision, except that fk_float is replaced by fk_double in pcc mode. If two different kinds have equivalent precisions, return the "higher" kind (e.g., if fk_double and fk_long_double are given, fk_long_double is returned even with configurations that use the same representation for both). This also implements the preference for extended floating point types, since their kinds follow those of the traditional floating point types. */ if (m_bits_1 > m_bits_2 || (m_bits_1 == m_bits_2 && fkind_1 > fkind_2)) { result = fkind_1; other = fkind_2; } else { result = fkind_2; other = fkind_1; } /* if */ if (is_extended_flt_kind(result)) { /* Special treatment of extended floating point types is required. */ if (num_mantissa_bits[(int)result] == num_mantissa_bits[(int)other] && max_exponent[(int)result] == max_exponent[(int)other] && flt_type_size[(int)result] == flt_type_size[(int)other] && ((other == (a_float_kind)fk_float && num_mantissa_bits[(int)fk_float] == num_mantissa_bits[(int)fk_double]) || (other == (a_float_kind)fk_double && num_mantissa_bits[(int)fk_double] == num_mantissa_bits[(int)fk_long_double]))) { /* If an extended floating point type has the same representation as more than one traditional type, the common type is "double". */ result = (a_float_kind)fk_double; } else if (is_extended_flt_kind(other) && result != other && flt_type_size[(int)result] == flt_type_size[(int)other]) { /* If two extended types have the same rank (size) but different representations, no common type exists. Report an error and proceed with "double". */ expr_pos_error(ec_no_common_type, &error_position); result = (a_float_kind)fk_double; } /* if */ } else if (C_dialect == C_dialect_pcc && result == (a_float_kind)fk_float) { result = (a_float_kind)fk_double; } /* if */ return result; } /* promoted_float_kind */ static a_type_ptr determine_arithmetic_conversions_full( an_operand *operand_1, a_type_ptr operand_1_type, an_operand *operand_2, a_type_ptr operand_2_type) /* Determine the "usual arithmetic conversions" on the operands to make them compatible, and return the type of the result. Note that this routine assumes that the type is arithmetic, and does not actually change the operand types. See section 6.1.2.5 of the ISO C89 standard. In C++, when wchar_t is a keyword, wchar_t is represented by one of the normal integral types and obeys the same conversion rules as its underlying type. Likewise for bool, char16_t, and char32_t. The operands can be lvalues or rvalues. If operand_1 is available only as a type, operand_1 == NULL and operand_1_type indicates the type. Likewise for operand_2/operand_2_type. Note that for some operations the operation type is different from the result type, and this routine does not determine a separate operation type. Also see determine_vector_operation_type and determine_imaginary_operation_type, which handle some special cases in GNU and C99 modes (with operations having vector operands or imaginary operands): If those routines return TRUE, this routine should not be called. */ { a_type_ptr type_1 = (operand_1 != NULL) ? operand_1->type : operand_1_type; a_type_ptr type_2 = (operand_2 != NULL) ? operand_2->type : operand_2_type; a_type_ptr result_type; an_integer_kind ikind_1, ikind_2; db_enter(4, "determine_arithmetic_conversions_full"); if (is_error_type(type_1) || is_error_type(type_2)) { result_type = error_type(); } else { a_float_kind fkind_1, fkind_2, result_fkind; /* Get past possible typerefs. */ type_1 = skip_typerefs(type_1); type_2 = skip_typerefs(type_2); fkind_1 = is_floating_type(type_1) ? type_1->variant.float_kind : (a_float_kind)fk_last; fkind_2 = is_floating_type(type_2) ? type_2->variant.float_kind : (a_float_kind)fk_last; result_fkind = promoted_float_kind(fkind_1, fkind_2); if (result_fkind != (a_float_kind)fk_last) { /* One of the operands had a (possibly complex) floating-point type. */ #if C99_IL_EXTENSIONS_SUPPORTED if (is_nonreal_floating_type(type_1) || is_nonreal_floating_type(type_2)) { /* If the operands have the same "type domain" (real, complex, or imaginary), the result has that same type domain. Otherwise, the result is complex. Note that we don't get here if both types are in the real domain; that's handled below. */ if (is_imaginary_type(type_1) && is_imaginary_type(type_2)) { result_type = imaginary_type(result_fkind); } else { result_type = complex_type(result_fkind); } /* if */ } else #endif /* C99_IL_EXTENSIONS_SUPPORTED */ /* Do not insert code here. */ { result_type = float_type(result_fkind); } /* if */ #if FIXED_POINT_ALLOWED } else if (fixed_point_enabled && (is_fixed_point_type(type_1) || is_fixed_point_type(type_2))) { /* At least one of the operands has a fixed-point type, and the other does not have a floating-point type. The result will have a fixed- point type, but no conversion is to be applied to the operands, except to turn an unsigned operand into a signed operand. */ result_type = fixed_point_result_type(type_1, type_2); #endif /* FIXED_POINT_ALLOWED */ } else { /* Neither operand has a floating-point or fixed-point type; do the integral promotions on both operands and try to get the result type from that. */ if (operand_1 != NULL) { type_1 = operand_type_after_integral_promotion(operand_1); } else { type_1 = type_after_integral_promotion(type_1); } /* if */ type_1 = skip_typerefs(type_1); if (operand_2 != NULL) { type_2 = operand_type_after_integral_promotion(operand_2); } else { type_2 = type_after_integral_promotion(type_2); } /* if */ type_2 = skip_typerefs(type_2); ikind_1 = is_integral_or_enum_type(type_1) ? type_1->variant.integer.int_kind : (an_integer_kind)ik_last; ikind_2 = is_integral_or_enum_type(type_2) ? type_2->variant.integer.int_kind : (an_integer_kind)ik_last; if (is_bit_precise_kind(ikind_1) || is_bit_precise_kind(ikind_2)) { a_targ_size_t width_1, width_2, size; a_targ_alignment alignment; a_boolean result_is_unsigned; if (is_bit_precise_kind(ikind_1)) { width_1 = bit_precise_integer_width(type_1); } else { get_integer_size_and_alignment(ikind_1, &size, &alignment); width_1 = size * targ_char_bit; } /* if */ if (is_bit_precise_kind(ikind_2)) { width_2 = bit_precise_integer_width(type_2); } else { get_integer_size_and_alignment(ikind_2, &size, &alignment); width_2 = size * targ_char_bit; } /* if */ result_is_unsigned = !int_kind_is_signed[(int)ikind_1] || !int_kind_is_signed[(int)ikind_2]; result_type = bit_precise_integer_type(max_val(width_1, width_2), result_is_unsigned, /*explicitly_signed=*/FALSE); goto done; } /* if */ #if INT128_EXTENSIONS_ALLOWED if (int128_extensions_enabled) { if (is_unsigned_int128(ikind_1) || is_unsigned_int128(ikind_2)) { /* If either operand has type "unsigned __int128", the other operand is converted to "unsigned __int128". */ result_type = integer_type((an_integer_kind)ik_unsigned_int128); goto done; } /* if */ if (is_int128(ikind_1) || is_int128(ikind_2)) { /* At least one operand has type "__int128". */ /* We assume no one wants to have 128-bit integers and also have long long be 128 bits, so we don't have to deal with other unsigned types whose values would not fit in __int128. */ check_assertion(targ_sizeof_int128 > targ_sizeof_long_long); /* One operand has type "__int128"; the other operand is converted to "__int128". */ result_type = integer_type((an_integer_kind)ik_int128); goto done; } /* if */ } /* if */ #endif /* INT128_EXTENSIONS_ALLOWED */ #if LONG_LONG_ALLOWED if (is_unsigned_long_long(ikind_1) || is_unsigned_long_long(ikind_2)) { /* If either operand has type "unsigned long long", the other operand is converted to "unsigned long long". */ result_type = integer_type((an_integer_kind)ik_unsigned_long_long); goto done; } /* if */ if (is_long_long(ikind_1) || is_long_long(ikind_2)) { /* At least one operand has type "long long". */ if (targ_sizeof_long_long == targ_sizeof_long) { /* A long long cannot represent all unsigned long values, so check for unsigned long values. */ if (is_unsigned_long(ikind_1) || is_unsigned_long(ikind_2)) { /* One operand has type "long long" and the other has type "unsigned long", and all values of "unsigned long" cannot be represented by "long long", so both operands are converted to "unsigned long long". */ result_type = integer_type((an_integer_kind)ik_unsigned_long_long); goto done; } /* if */ } /* if */ if (targ_sizeof_long_long == targ_sizeof_int) { /* A long long cannot represent all unsigned int values, so check for unsigned int values. */ if (is_unsigned_int(ikind_1) || is_unsigned_int(ikind_2)) { /* One operand has type "long long" and the other has type "unsigned int", and all values of "unsigned int" cannot be represented by "long long", so both operands are converted to "unsigned long long". */ result_type = integer_type((an_integer_kind)ik_unsigned_long_long); goto done; } /* if */ } /* if */ /* One operand has type "long long"; the other operand is converted to "long long". */ result_type = integer_type((an_integer_kind)ik_long_long); goto done; } /* if */ #endif /* LONG_LONG_ALLOWED */ if (is_unsigned_long(ikind_1) || is_unsigned_long(ikind_2)) { /* If either operand has type "unsigned long", the other operand is converted to "unsigned long". */ result_type = integer_type((an_integer_kind)ik_unsigned_long); } else if (is_long(ikind_1) || is_long(ikind_2)) { /* At least one operand has type "long". */ if (long_preserving_rules) { /* In K&R I, Appendix A, 6.6, there is no special case based on the size of long vs. int, so "long + unsigned int" has a result type of long even if long and int have the same size. */ } else if (targ_sizeof_long == targ_sizeof_int) { /* A "long" cannot represent all "unsigned int" values, so check for "unsigned int" values. */ if (is_unsigned_int(ikind_1) || is_unsigned_int(ikind_2)) { /* One operand has type "long" and the other has type "unsigned int", and all values of "unsigned int" cannot be represented by "long", so both operands are converted to "unsigned long". */ result_type = integer_type((an_integer_kind)ik_unsigned_long); goto done; } /* if */ } /* if */ /* One operand has type "long"; the other operand is converted to "long". */ result_type = integer_type((an_integer_kind)ik_long); } else if (is_unsigned_int(ikind_1) || is_unsigned_int(ikind_2)) { /* If either operand has type "unsigned int", the other operand is converted to "unsigned int". */ result_type = integer_type((an_integer_kind)ik_unsigned_int); } else { /* Otherwise, both operands are converted to "int". */ result_type = integer_type((an_integer_kind)ik_int); } /* if */ } /* if */ } /* if */ done:; #if MICROSOFT_EXTENSIONS_ALLOWED if (microsoft_mode && is_integral_or_enum_type(result_type)) { /* Preserve the microsoft_sized_int_type flag in the result type if it is there in the operand types. That prevents returning, e.g., long long when the right answer is __int64. */ a_boolean result_is_microsoft_sized_int = FALSE; a_type_ptr base_result_type = skip_typerefs(result_type); a_type_ptr base_type_1 = skip_typerefs(type_1); a_type_ptr base_type_2 = skip_typerefs(type_2); a_boolean type_1_same_int_as_result = (is_integral_or_enum_type(base_type_1) && base_type_1->variant.integer.int_kind == base_result_type->variant.integer.int_kind); a_boolean type_2_same_int_as_result = (is_integral_or_enum_type(base_type_2) && base_type_2->variant.integer.int_kind == base_result_type->variant.integer.int_kind); if (type_1_same_int_as_result && type_2_same_int_as_result) { if (base_type_1->variant.integer.microsoft_sized_int_type && base_type_2->variant.integer.microsoft_sized_int_type) { result_is_microsoft_sized_int = TRUE; } /* if */ } else if (type_1_same_int_as_result) { if (base_type_1->variant.integer.microsoft_sized_int_type) { result_is_microsoft_sized_int = TRUE; } /* if */ } else if (type_2_same_int_as_result) { if (base_type_2->variant.integer.microsoft_sized_int_type) { result_is_microsoft_sized_int = TRUE; } /* if */ } /* if */ if (result_is_microsoft_sized_int) { result_type = microsoft_sized_integer_type( base_result_type->variant.integer.int_kind); } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ db_exit(); return result_type; } /* determine_arithmetic_conversions_full */ a_type_ptr determine_arithmetic_conversions(an_operand *operand_1, an_operand *operand_2) /* Determine the "usual arithmetic conversions" on the operands to make them compatible, and return the type of the result. This is an interface to determine_arithmetic_conversions_full for a simple case; see the header comment of that routine for details. */ { a_type_ptr result_type; result_type = determine_arithmetic_conversions_full(operand_1, (a_type_ptr)NULL, operand_2, (a_type_ptr)NULL); return result_type; } /* determine_arithmetic_conversions */ a_type_ptr usual_arithmetic_conversions(a_type_ptr operand_1_type, a_type_ptr operand_2_type) /* Determine the "usual arithmetic conversions" on the indicated operand types to make them compatible, and return the type of the result. This is an interface to determine_arithmetic_conversions_full for a simple case; see the header comment of that routine for details. */ { a_type_ptr result_type; result_type = determine_arithmetic_conversions_full((an_operand *)NULL, operand_1_type, (an_operand *)NULL, operand_2_type); return result_type; } /* usual_arithmetic_conversions */ void check_binary_scoped_enum_operation(an_operand *operand_1, an_operand *operand_2, a_type_ptr *p_operation_type) /* A built-in binary operation is being performed on the given two operands, at least one of which is a scoped enum value. Issue an error if the two operands do not have the same type and return the scoped enumeration type of either operand through *p_operation_type. */ { check_assertion(is_scoped_enum_type(operand_1->type) || is_scoped_enum_type(operand_2->type)); *p_operation_type = skip_typerefs(operand_1->type); if (!identical_types_ignoring_qualifiers(operand_1->type, operand_2->type)) { if (!is_scoped_enum_type(operand_1->type)) { error_in_operand(ec_scoped_enum_operation_type_mismatch, operand_1); *p_operation_type = skip_typerefs(operand_2->type); } else { error_in_operand(ec_scoped_enum_operation_type_mismatch, operand_2); } /* if */ } /* if */ } /* check_binary_scoped_enum_operation */ #if TARG_HAS_IEEE_FLOATING_POINT STATIC_THREAD a_constant_ptr nan_constant; /* The floating point __NAN__ constant (initialized lazily by make_nan_operand). */ void make_nan_operand(an_operand *result) /* Create an operand for the constant __NAN__, which represents a float Not-a-Number value. The identifier __NAN__ is EDG-specific (i.e., not specified by the C99 standard). The standard specifies a macro NAN that expands to this value. */ { if (nan_constant == (a_constant_ptr)NULL) { nan_constant = fs_constant((a_constant_repr_kind)ck_float); nan_constant->type = float_type((a_float_kind)fk_float); (void)make_fp_nan(&nan_constant->variant.float_value, (a_float_kind)fk_float, /*signaling=*/FALSE, (an_fp_value_part)0); } /* if */ make_constant_operand(nan_constant, result); } /* make_nan_operand */ STATIC_THREAD a_constant_ptr infinity_constant; /* The floating point __INFINITY__ constant (initialized lazily by make_infinity_operand). */ void make_infinity_operand(an_operand *result) /* Create an operand for the constant __INFINITY__, which represents a float positive Infinity value. The identifier __INFINITY__ is EDG-specific (i.e., not specified by the C99 standard). The standard specifies a macro INFINITY that expands to this value. */ { if (infinity_constant == (a_constant_ptr)NULL) { infinity_constant = fs_constant((a_constant_repr_kind)ck_float); infinity_constant->type = float_type((a_float_kind)fk_float); (void)make_fp_infinity(&infinity_constant->variant.float_value, (a_float_kind)fk_float); } /* if */ make_constant_operand(infinity_constant, result); } /* make_infinity_operand */ #endif /* TARG_HAS_IEEE_FLOATING_POINT */ #if C99_IL_EXTENSIONS_SUPPORTED STATIC_THREAD a_constant_ptr imaginary_unit; /* The floating point __I__ constant (initialized lazily by make_imaginary_unit_operand). */ void make_imaginary_unit_operand(an_operand *result) /* Create an operand for the constant __I__ such that __I__*__I__ == -1. The identifier __I__ is EDG-specific (i.e., not specified by the C99 standard). The standard specifies macros I and _Imaginary_I that expand to this value. In non-GNU C99 mode, the type of this operand is _Imaginary, but in GNU C/C99/C++ modes it is _Complex. */ { if (imaginary_unit == (a_constant_ptr)NULL) { a_boolean err = FALSE; if (c99_mode && !gcc_mode) { imaginary_unit = fs_constant((a_constant_repr_kind)ck_imaginary); imaginary_unit->type = imaginary_type((a_float_kind)fk_float); fp_host_large_integer_to_float((a_float_kind)fk_float, (a_host_large_integer)1, &imaginary_unit->variant.float_value, &err); } else { imaginary_unit = fs_constant((a_constant_repr_kind)ck_complex); imaginary_unit->type = complex_type((a_float_kind)fk_float); fp_host_large_integer_to_float( (a_float_kind)fk_float, (a_host_large_integer)0, &imaginary_unit->variant.complex_value->real, &err); fp_host_large_integer_to_float( (a_float_kind)fk_float, (a_host_large_integer)1, &imaginary_unit->variant.complex_value->imag, &err); } /* if */ check_assertion(!err); } /* if */ make_constant_operand(imaginary_unit, result); } /* make_imaginary_unit_operand */ static void promote_operand_for_imaginary_operation(an_operand *operand, a_float_kind new_fkind) /* Promote the given operand to a floating-point type precision specified by new_fkind. Preserve the domain (real or imaginary) of the operand. */ { a_type_ptr type = skip_typerefs(operand->type); a_float_kind fkind = is_floating_type(type) ? type->variant.float_kind : (a_float_kind)fk_last; check_assertion(type->kind != (a_type_kind)tk_complex); if (new_fkind != fkind) { a_type_ptr promoted_type; if (type->kind == (a_type_kind)tk_imaginary) { promoted_type = imaginary_type(new_fkind); } else { promoted_type = float_type(new_fkind); } /* if */ cast_operand(promoted_type, operand, /*is_implicit_cast=*/TRUE); } /* if */ } /* promote_operand_for_imaginary_operation */ a_boolean determine_imaginary_operation_type (a_token_kind op_token, an_operand *operand_1, an_operand *operand_2, a_source_position *err_pos, a_type_ptr *result_type, an_expr_operator_kind *op) /* See whether the operation represented by op_token applied to operand_1 and operand_2 is one of the special cases involving imaginary types that has an operation type different than the one determined by the usual arithmetic conversions. If so, convert the operands to the proper operation type, set *result_type to the result type, set *op to the IL operator to be used, and return TRUE. Otherwise, return FALSE, set *op to eok_last, and leave *result_type unchanged. err_pos gives a source position for any errors. */ { a_boolean is_special = FALSE; a_boolean is_imaginary_1, is_imaginary_2; a_type_ptr type_1 = skip_typerefs(operand_1->type), type_2 = skip_typerefs(operand_2->type); a_float_kind fkind_1, fkind_2; a_float_kind fkind_result = (a_float_kind)fk_float; a_boolean is_compound_assignment = FALSE; switch (op_token) { case tok_plus: case tok_minus: case tok_plus_assign: case tok_minus_assign: is_imaginary_1 = is_imaginary_type(type_1); is_imaginary_2 = is_imaginary_type(type_2); if (!is_imaginary_1 && !is_imaginary_2) { /* No imaginary types, so nothing special to check. */ } else if (is_imaginary_1 && is_imaginary_2) { /* Imaginary + imaginary gives imaginary. Likewise for -. */ is_special = TRUE; fkind_1 = type_1->variant.float_kind; fkind_2 = type_2->variant.float_kind; fkind_result = promoted_float_kind(fkind_1, fkind_2); *result_type = imaginary_type(fkind_result); /* Determine the IL operator to use. */ switch (op_token) { case tok_plus: *op = (an_expr_operator_kind)eok_add; break; case tok_minus: *op = (an_expr_operator_kind)eok_subtract; break; case tok_plus_assign: *op = (an_expr_operator_kind)eok_add_assign; is_compound_assignment = TRUE; break; case tok_minus_assign: *op = (an_expr_operator_kind)eok_subtract_assign; is_compound_assignment = TRUE; break; /* coverity[dead_error_begin] */ default: unexpected_condition(); } /* switch */ } else if ((op_token == tok_plus || op_token == tok_minus) && ((is_imaginary_1 && is_real_floating_type(type_2)) || (is_imaginary_2 && is_real_floating_type(type_1)))) { /* Real + imaginary and similar combinations have special operators. We do not convert first to complex and then add or subtract, because (a) it's less efficient, and (b) it destroys negative zeroes. See G.5.2 in the C99 standard. Note that += and -= are not handled specially. */ is_special = TRUE; fkind_1 = type_1->variant.float_kind; fkind_2 = type_2->variant.float_kind; fkind_result = promoted_float_kind(fkind_1, fkind_2); *result_type = complex_type(fkind_result); /* Determine the IL operator to use. */ if (op_token == tok_plus) { if (is_imaginary_2) { /* Real + imaginary. */ *op = (an_expr_operator_kind)eok_fjadd; } else { /* Imaginary + real. */ *op = (an_expr_operator_kind)eok_jfadd; } /* if */ } else { if (is_imaginary_2) { /* Real - imaginary. */ *op = (an_expr_operator_kind)eok_fjsubtract; } else { /* Imaginary - real. */ *op = (an_expr_operator_kind)eok_jfsubtract; } /* if */ } /* if */ } /* if */ break; case tok_star: case tok_divide: case tok_times_assign: case tok_divide_assign: is_imaginary_1 = is_imaginary_type(type_1); is_imaginary_2 = is_imaginary_type(type_2); if (!is_imaginary_1 && !is_imaginary_2) { /* No imaginary types, so nothing special to check. */ } else if (is_imaginary_1 && is_imaginary_2) { /* Imaginary * imaginary gives real. Likewise for /. */ is_special = TRUE; fkind_1 = type_1->variant.float_kind; fkind_2 = type_2->variant.float_kind; fkind_result = promoted_float_kind(fkind_1, fkind_2); *result_type = float_type(fkind_result); } else if ((is_imaginary_1 && is_real_floating_type(type_2)) || (is_imaginary_2 && is_real_floating_type(type_1))) { /* Imaginary * real gives imaginary, and likewise for the similar cases. */ is_special = TRUE; fkind_1 = type_1->variant.float_kind; fkind_2 = type_2->variant.float_kind; fkind_result = promoted_float_kind(fkind_1, fkind_2); *result_type = imaginary_type(fkind_result); } else if (is_imaginary_1 && is_integral_or_enum_type(type_2)) { /* Imaginary * integer gives imaginary, and likewise for the similar cases. */ is_special = TRUE; fkind_result = type_1->variant.float_kind; *result_type = imaginary_type(fkind_result); } else if (is_imaginary_2 && is_integral_or_enum_type(type_1)) { /* Integer * imaginary gives imaginary, and likewise for the similar cases. */ is_special = TRUE; fkind_result = type_2->variant.float_kind; *result_type = imaginary_type(fkind_result); } /* if */ /* Determine the IL operator to use. */ if (is_special) { switch (op_token) { case tok_star: if (is_imaginary_1 && is_imaginary_2) { /* Imaginary * imaginary has a special operator, which is like a float multiplication followed by a negation. */ *op = (an_expr_operator_kind)eok_jmultiply; } else { *op = (an_expr_operator_kind)eok_multiply; } /* if */ break; case tok_divide: if (is_imaginary_2 && is_real_floating_type(type_1)) { /* A special operator that includes a sign inversion is needed when dividing a real number by an imaginary number. x/(y*__I__) == -(x/y)*__I__ */ *op = (an_expr_operator_kind)eok_jdivide; } else { *op = (an_expr_operator_kind)eok_divide; } /* if */ break; case tok_times_assign: if (is_imaginary_1 && is_imaginary_2) { /* Imaginary *= imaginary gives zero, so it's almost surely wrong. */ expr_pos_warning(ec_imaginary_times_assign, err_pos); /* Change the expression to one with a value of zero. Use a comma expression to preserve side-effects. */ { an_expr_node_ptr node2 = make_node_from_operand(operand_2); a_constant_ptr con = local_constant(); make_zero_of_proper_type(node2->type, con); node2 = make_comma_node(node2, alloc_node_for_constant(con)); make_expression_operand(node2, operand_2); release_local_constant(&con); } } /* if */ *op = (an_expr_operator_kind)eok_multiply_assign; is_compound_assignment = TRUE; break; case tok_divide_assign: *op = (an_expr_operator_kind)eok_divide_assign; is_compound_assignment = TRUE; break; default: unexpected_condition(); } /* switch */ } /* if */ break; default: /* Nothing special about the other operators. */ break; } /* switch */ if (is_special) { /* Convert the operands to the operation type. For compound assignment operators, convert only the second operand. */ if (!is_compound_assignment) { promote_operand_for_imaginary_operation(operand_1, fkind_result); } /* if */ promote_operand_for_imaginary_operation(operand_2, fkind_result); } else { *op = (an_expr_operator_kind)eok_last; } /* if */ return is_special; } /* determine_imaginary_operation_type */ #endif /* C99_IL_EXTENSIONS_SUPPORTED */ #if GNU_VECTOR_TYPES_ALLOWED a_type_ptr make_integer_vector_result_type(a_type_ptr operand_type) /* Returns the type to be used for GNU vector operations that return "a vector of the same width and number of elements as the comparison operands with a signed integral element type." Used for relational and logical operators. It appears that the integer type used for the elements is a signed integer whose size matches that of the operand vector elements. The resulting type mirrors the operand's vector kind (vk_gnu, vk_ext, ...). Clang in particular keeps a comparison of two ext_vector_type operands as another ext_vector_type vector. For vk_ext operands we additionally carry over the size_constant. */ { an_integer_kind int_kind; a_type_ptr result, elem_type; if (is_error_type(operand_type)) { result = error_type(); } else { operand_type = skip_typerefs(operand_type); check_assertion(type_is(operand_type, tk_vector)); elem_type = skip_typerefs(operand_type->variant.vector.element_type); int_kind = int_kind_for_size_and_alignment(elem_type->size, elem_type->alignment, /*is_signed=*/TRUE); if (int_kind == ik_last) { pos_ty_error(ec_bad_vector_float_operand_size, &error_position, operand_type); result = error_type(); } else { a_vector_kind vkind = operand_type->variant.vector.kind; result = make_vector_type(integer_type(int_kind), num_vector_elements(operand_type), vkind); if (vkind == vk_ext) { result->variant.vector.size_constant = operand_type->variant.vector.size_constant; } /* if */ } /* if */ } /* if */ return result; } /* make_integer_vector_result_type */ a_boolean vector_and_scalar_types_are_compatible(a_type_ptr vec_type, a_type_ptr scalar_type, a_constant_ptr scalar_con, a_boolean narrowing_okay) /* Returns TRUE if the specified vector type and the scalar type can be mixed in an operation. In cases where scalar_type is the type of a constant being used in the operation, scalar_con is the constant (which can be NULL). Used for operations like "a = b + 1" which corresponds to "a = b + {1,1,1,1}" assuming "a" and "b" are vectors of four integer types. narrowing_okay is TRUE if a narrowing conversion from the scalar type to the vector element type is acceptable. */ { a_boolean result; a_type_ptr elem_type; check_assertion(is_vector_type(vec_type)); vec_type = skip_typerefs(vec_type); elem_type = skip_typerefs(vec_type->variant.vector.element_type); if (is_template_param_type(elem_type) || is_template_param_type(scalar_type)) { result = TRUE; } else if ((type_is(elem_type, tk_integer) || type_is(elem_type, tk_float)) && (elem_type->kind == scalar_type->kind || scalar_type->kind == (a_type_kind)tk_integer) && !is_immediate_enum_type(scalar_type) && (narrowing_okay || !is_narrowing_conversion(scalar_type, scalar_con, elem_type, /*check_enum_target=*/FALSE, (an_error_code*)NULL))) { result = TRUE; } else { result = FALSE; } /* if */ return result; } /* vector_and_scalar_types_are_compatible */ void make_vector_fill_operand(an_operand *operand, a_type_ptr vec_type) /* The specified operand is a scalar that is being "promoted" to a vector of the specified type (where each element of the resulting vector has the value specified by operand). Add a compiler-generated eok_vector_fill operation on top of the operand (in place) unless the operand is a template parameter (in which case no transformation is done). */ { an_expr_node_ptr expr; a_source_position save_position = operand->position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position save_end_position = operand->end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ check_assertion(!is_vector_type(operand->type) && is_vector_type(vec_type)); if (!is_template_param_type(operand->type)) { a_type_ptr elem_type = skip_typerefs(vec_type)->variant.vector.element_type; if (!identical_types_ignoring_qualifiers(operand->type, elem_type)) { /* Convert the scalar operand type to the type of the vector element. */ cast_operand(elem_type, operand, /*is_implicit_cast=*/TRUE); } /* if */ expr = make_node_from_operand(operand); expr = make_operator_node((an_expr_operator_kind)eok_vector_fill, vec_type, expr); make_expression_operand(expr, operand); /* This is a compiler-generated operand. */ expr->compiler_generated = TRUE; operand->position = save_position; #if EXTRA_SOURCE_POSITIONS_IN_IL operand->end_position = save_end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* if */ } /* make_vector_fill_operand */ a_boolean determine_vector_operation_type( a_token_kind op_token, an_operand *operand_1, an_operand *operand_2, a_source_position *err_pos, a_type_ptr *operation_type, an_expr_operator_kind *op) /* Check whether the operation represented by op_token applied to operand_1 and operand_2 is a vector operation. If so, set *operation_type to the type of the operation, *op to the IL operator kind (eok_...) representing this operation, and return TRUE. If the operand types involve at least one vector type, but the operand types are not valid for a vector operation, issue an error at *err_pos and set *op to eok_error and *operation_type to an error type (TRUE is still returned in such cases). Note that in some cases (notably relational and logical operations), the result of the operation (determined by the caller and not here) may be different than the type of the operation. Also, in mixed signed/unsigned operations, the operands may be converted to a common (unsigned) type. */ { a_type_ptr op1_type = skip_typerefs(operand_1->type); a_type_ptr op2_type = skip_typerefs(operand_2->type); a_boolean op1_is_vec = is_vector_type(op1_type); a_boolean op2_is_vec = is_vector_type(op2_type); a_boolean is_vector_operation = TRUE, mixed_signedness = FALSE; if (!op1_is_vec && !op2_is_vec) { /* Neither operand has a vector type. */ is_vector_operation = FALSE; } else if (op1_is_vec && op2_is_vec) { /* Both operands have vector type. */ a_type_ptr el1_type = op1_type->variant.vector.element_type; a_type_ptr el2_type = op2_type->variant.vector.element_type; if (op1_type->size != op2_type->size) { expr_pos_error(ec_vectors_must_have_same_size, err_pos); *operation_type = error_type(); *op = (an_expr_operator_kind)eok_error; } else if (!identical_types_ignoring_qualifiers(el1_type, el2_type) && !((op1_type->variant.vector.is_boolean_vector || op2_type->variant.vector.is_boolean_vector) && num_vector_elements(op1_type) == num_vector_elements(op2_type) && size_of_type(op1_type) == size_of_type(op2_type)) && !(is_integral_type(el1_type) && is_integral_type(el2_type) && (mixed_signedness = integral_types_the_same_except_for_signedness( el1_type, el2_type)))) { /* Either the element types must be identical, or they must be integer types that differ only in signedness. An additional permitted combination is one where at least one of the vectors is the result of a comparison (making it "boolean") and the number of elements in both vectors is equal. */ expr_pos_error(ec_vector_element_type_mismatch, err_pos); *operation_type = error_type(); *op = (an_expr_operator_kind)eok_error; } else { switch (op_token) { case tok_ampersand: case tok_excl_or: case tok_or: case tok_remainder: case tok_and_assign: case tok_excl_or_assign: case tok_or_assign: case tok_remainder_assign: if ((!is_integral_or_enum_type(el1_type) && !is_template_param_type(el1_type)) || (!is_integral_or_enum_type(el2_type) && !is_template_param_type(el2_type))) { expr_pos_error(ec_vector_operation_requires_integer_vector, err_pos); *operation_type = error_type(); *op = (an_expr_operator_kind)eok_error; break; } /* if */ FALLTHROUGH default: *operation_type = op1_type; *op = which_binary_operator(op_token, op1_type); if (mixed_signedness && is_signed_integral_type(el1_type)) { /* The vector types differ only in the signedness of their (integral) elements. The result type is the unsigned type, and if the operation is a compound assignment the first operand must be the unsigned operand. */ if (is_compound_assignment_operator(*op)) { expr_pos_error(ec_vector_element_type_mismatch, err_pos); *operation_type = error_type(); *op = (an_expr_operator_kind)eok_error; } else { *operation_type = op2_type; cast_operand(op2_type, operand_1, /*is_implicit_cast=*/TRUE); } /* if */ } else { cast_operand(op1_type, operand_2, /*is_implicit_cast=*/TRUE); } /* if */ } /* switch */ } /* if */ } else { /* One operand has vector type. If the other operand has a proper scalar type, allow the operation (e.g., "a = b + 1" is equivalent to "a = b + {1, 1, 1, 1}" for a vector of four integers). If the types are found to be "compatible", use an eok_vector_fill operation to "promote" the scalar to a vector type. */ a_type_ptr vec_type, scalar_type; a_constant_ptr con = NULL; a_boolean narrowing_okay = (op_token == tok_shift_left || op_token == tok_shift_right); an_operand *scalar_opnd; if (op1_is_vec) { vec_type = op1_type; scalar_type = op2_type; scalar_opnd = operand_2; } else { vec_type = op2_type; scalar_type = op1_type; scalar_opnd = operand_1; } /* if */ if (is_constant_operand(scalar_opnd)) { con = &scalar_opnd->variant.constant; } /* if */ if (op_token == tok_and_and || op_token == tok_or_or) { /* Allow a mixed-type logical operations. An initial scalar operand may serve to short-circuit the evaluation of the second operand (and the scalar is never converted to a vector in any of the GCC-specified rewrites even if it is specified as the second operand). */ *operation_type = vec_type; *op = which_binary_operator(op_token, *operation_type); } else if (vector_and_scalar_types_are_compatible(vec_type, scalar_type, con, narrowing_okay) && !is_a_glvalue(scalar_opnd)) { /* The vector and scalar types are compatible. Convert the scalar operand to a vector. (The "is_a_glvalue" test guards against situations like "scalar += vector".) */ a_type_ptr el_type = vec_type->variant.vector.element_type; make_vector_fill_operand(scalar_opnd, vec_type); switch (op_token) { case tok_ampersand: case tok_excl_or: case tok_or: case tok_remainder: case tok_and_assign: case tok_excl_or_assign: case tok_or_assign: case tok_remainder_assign: if ((!is_integral_or_enum_type(el_type) && !is_template_param_type(el_type)) || (!is_integral_or_enum_type(scalar_type) && !is_template_param_type(scalar_type))) { expr_pos_error(ec_vector_operation_requires_integer_vector, err_pos); *operation_type = error_type(); *op = (an_expr_operator_kind)eok_error; break; } /* if */ FALLTHROUGH default: *operation_type = vec_type; *op = which_binary_operator(op_token, *operation_type); } /* switch */ } else { /* Not an allowed mixed-type operation. */ expr_pos_error(ec_mixed_vector_scalar_operation, err_pos); *operation_type = error_type(); *op = (an_expr_operator_kind)eok_error; } /* if */ } /* if */ return is_vector_operation; } /* determine_vector_operation_type */ #endif /* GNU_VECTOR_TYPES_ALLOWED */ #if MICROSOFT_EXTENSIONS_ALLOWED void adjust_constant_operand_info_for_microsoft_null_pointer_test( an_operand *operand, a_boolean *operand_is_constant, a_constant **operand_constant, an_expr_node_ptr *con_expr) /* In Microsoft mode, expressions like (x, 0) are allowed as null pointer constants. This routine examines *operand to see if it is such a thing, and sets *operand_is_constant and *operand_constant to the underlying null pointer constant. Those are then presumably passed to a function like impl_conversion_possible, which would then conclude that it is possible to implicitly convert the *operand expression to a pointer type. This routine is called only in Microsoft mode. If con_expr is non-NULL, *con_expr is set to point to the expression node that points to the null pointer constant returned in *operand_constant. */ { *operand_is_constant = FALSE; if (is_expression_operand(operand)) { an_expr_node_ptr expr = skip_parens(operand->variant.expression); if (is_operation_node(expr) && expr->variant.operation.kind == (an_expr_operator_kind)eok_comma) { /* The operand is a comma expression. */ expr = skip_parens(expr->variant.operation.operands->next); if (is_constant_node(expr) && is_or_might_be_null_pointer_constant(node_constant(expr))) { /* The operand is a comma node with a second operand that is a null pointer constant, e.g., (x, 0). */ *operand_is_constant = TRUE; *operand_constant = node_constant(expr); if (con_expr != NULL) *con_expr = expr; } /* if */ } /* if */ } /* if */ } /* adjust_constant_operand_info_for_microsoft_null_pointer_test */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ a_boolean operand_is_function(an_operand *operand) /* Return TRUE if the operand is an lvalue for or address of a specific function, but not an arbitrary pointer to function. Likewise for a pointer-to-member for a specific function. */ { a_boolean is_function = FALSE; if (routine_from_function_operand(operand) != NULL) { is_function = TRUE; } else if (is_constant_operand(operand)) { a_constant_ptr con = &operand->variant.constant; if (con->kind == (a_constant_repr_kind)ck_ptr_to_member && con->variant.ptr_to_member.is_function_ptr && con->variant.ptr_to_member.variant.routine != NULL) { is_function = TRUE; } /* if */ } /* if */ return is_function; } /* operand_is_function */ static a_boolean op_is_null_ptr_constant_for_comparison(an_operand *opnd) /* Return TRUE if the given operand should be treated as a null pointer constant for a comparison operation. Ordinarily, this produces the same result as op_is_null_pointer_constant, but in GNU and Microsoft modes some additional cases must be handled. */ { a_boolean result = FALSE; if (op_is_null_pointer_constant(opnd)) { result = !cppcli_enabled || is_plain_pointer_type(opnd->type); } else if (gpp_mode && is_constant_operand(opnd)) { /* GCC 6.0 and later implement Core issue 903, which makes "false" invalid as a null pointer constant, but in comparison contexts the older behavior persists. */ a_constant_ptr cp = &opnd->variant.constant; if (constant_is(cp, ck_integer) && !cp->null_pointer_constant_ruled_out && is_bool_type(cp->type) && cmplit_integer_constant(cp, (a_host_large_integer)0) == 0) { result = TRUE; } /* if */ } /* if */ return result; } /* op_is_null_ptr_constant_for_comparison */ a_boolean check_compatibility_of_pointer_operands( an_operand *operand_1, an_operand *operand_2, a_source_position *operator_position, an_opname_kind opkind, a_boolean pointer_normalization_standard_in_C, a_boolean pointers_to_functions_standard_in_C, a_boolean pointers_to_incomplete_standard_in_C, a_boolean mixed_object_and_incomplete_standard_in_C, a_type_ptr *p_operation_type) /* operand_1 and operand_2 are the operands of a pointer operation (described by opkind). Check to see that the operands are compatible or can be made compatible. One or the other of the operands, or both, must have a pointer type. Return the operation type in *p_operation_type. (The operands are not cast to the operation type; the caller must do that.) operator_position gives the operator position (for errors). The other switches indicate the legality of certain constructs in ANSI C. "Illegal" constructs are accepted anyway, but warnings are issued in strict ANSI C mode. The switches are used only in strict ANSI mode. Return FALSE if there is an error. */ { a_boolean okay = FALSE; a_type_ptr operand_1_type = operand_1->type; a_type_ptr operand_2_type = operand_2->type; a_type_ptr operation_type = NULL; a_boolean operand_1_is_pointer = is_pointer_type(operand_1_type); a_boolean operand_2_is_pointer = is_pointer_type(operand_2_type); a_boolean suppress_extensions; a_std_conv_descr std_conv; if (!C_mode() && (opname_is_comparison(opkind) || opkind == (an_opname_kind)onk_question)) { /* For equality operators, relational operators, and the ?: operator, the compatibility rules were revised through the resolution of Core issue 1512 (the C++ committee's paper N3624). The new rules apply to all C++ modes. They also apply to the C++20 spaceship operator. */ if (op_is_null_ptr_constant_for_comparison(operand_1)) { if (op_is_null_pointer_constant(operand_2)) { /* Two null pointer constants. We usually do not get here since null pointer constants do not usually have pointer types. In some GNU modes, however, they do. */ check_assertion(!strict_ansi_mode); if (operand_1_is_pointer) { if (!operand_2_is_pointer) { okay = TRUE; operation_type = operand_1_type; } else { /* Two pointers: Fall back to the non-null-pointer-constant rule. */ operation_type = make_cv_combined_type_if_possible( operand_1_type, operand_2_type); okay = operation_type != NULL; } /* if */ } else { check_assertion(operand_2_is_pointer); okay = TRUE; operation_type = operand_2_type; } /* if */ } else { okay = TRUE; operation_type = operand_2_type; } /* if */ } else if (op_is_null_ptr_constant_for_comparison(operand_2)) { okay = TRUE; operation_type = operand_1_type; } else { operation_type = make_cv_combined_type_if_possible( operand_1_type, operand_2_type); okay = operation_type != NULL; } /* if */ if (!okay && !strict_ansi_mode && opkind != (an_opname_kind)onk_question && operand_1_is_pointer && operand_2_is_pointer) { /* A pointer to void and a pointer to function are not normally comparable, but many compilers do permit it. */ a_type_ptr stp1, stp2, ustp1, ustp2; stp1 = skip_typerefs(operand_1_type)->variant.pointer.type; stp2 = skip_typerefs(operand_2_type)->variant.pointer.type; ustp1 = skip_typerefs(stp1); ustp2 = skip_typerefs(stp2); if (ustp1->kind == (a_type_kind)tk_void && ustp2->kind == (a_type_kind)tk_routine) { operation_type = make_pointer_type( make_qualified_type(stp1, get_type_qualifiers(stp2))); okay = TRUE; } else if (ustp1->kind == (a_type_kind)tk_routine && ustp2->kind == (a_type_kind)tk_void) { operation_type = make_pointer_type( make_qualified_type(stp2, get_type_qualifiers(stp1))); okay = TRUE; } /* if */ if (operation_type != NULL) { expr_pos_warning(ec_comparison_of_pointers_to_void_and_function, operator_position); } /* if */ } /* if */ if (microsoft_mode && !okay) { /* In Microsoft mode, if no compatibility was found, try again using the method below. */ } else { if (!okay) { expr_pos_ty2_error(ec_incompatible_operands, operator_position, operand_1_type, operand_2_type); operation_type = error_type(); } /* if */ goto done; } /* if */ } /* if */ /* The loop here tries the conversions once without extensions allowed, and (if that fails) again with extensions allowed, so we won't pick a conversion direction that requires an extension when the opposite direction doesn't. */ suppress_extensions = TRUE; for (;;) { if (operand_1_is_pointer) { a_boolean operand_2_is_constant = is_constant_operand(operand_2); a_constant_ptr operand_2_constant = &operand_2->variant.constant; #if MICROSOFT_EXTENSIONS_ALLOWED if (microsoft_mode && !operand_2_is_constant) { /* Microsoft mode allows some expressions as null pointer constants. */ adjust_constant_operand_info_for_microsoft_null_pointer_test( operand_2, &operand_2_is_constant, &operand_2_constant, (an_expr_node **)NULL); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* See if the second operand can be converted to the type of the first operand. */ if (C_mode() && operand_2_is_pointer && ((is_constant_operand(operand_1) && is_null_pointer_constant(&operand_1->variant.constant)) || (is_void_type(type_pointed_to(operand_2_type)) && !(is_constant_operand(operand_2) && is_null_pointer_constant(&operand_2->variant.constant))))) { /* The first operand is a C null pointer constant of (void *)0. Don't try to convert the second operand to void *, because the conversion in the other direction should be preferred. Or, the second operand has type void *, but it's not a null pointer constant of (void *)0. Don't try to convert it to the type of the first operand, because the conversion in the other direction should be preferred. */ } else if (impl_pointer_conversion(operand_2_type, operand_2_is_constant, (a_boolean)operand_2-> is_simple_string_literal, operand_is_function(operand_2), operand_2_constant, operand_1_type, /*allow_qualifier_or_eh_mismatch=*/TRUE, suppress_extensions, ec_incompatible_operands, &std_conv)) { operation_type = operand_1_type; okay = TRUE; break; } /* if */ } /* if */ if (operand_2_is_pointer) { a_boolean operand_1_is_constant = is_constant_operand(operand_1); a_constant_ptr operand_1_constant = &operand_1->variant.constant; #if MICROSOFT_EXTENSIONS_ALLOWED if (microsoft_mode && !operand_1_is_constant) { /* Microsoft mode allows some expressions as null pointer constants. */ adjust_constant_operand_info_for_microsoft_null_pointer_test( operand_1, &operand_1_is_constant, &operand_1_constant, (an_expr_node **)NULL); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (impl_pointer_conversion(operand_1_type, operand_1_is_constant, (a_boolean)operand_1-> is_simple_string_literal, operand_is_function(operand_1), operand_1_constant, operand_2_type, /*allow_qualifier_or_eh_mismatch=*/TRUE, suppress_extensions, ec_incompatible_operands, &std_conv)) { operation_type = operand_2_type; okay = TRUE; break; } /* if */ } /* if */ if (suppress_extensions == FALSE) { /* Stop after second time around loop. */ break; } else if (!C_mode()) { /* In C++, multilevel pointer types are compatible if they differ only in the various qualifications. The composite type picks the unions of those qualifiers. */ operation_type = multilevel_composite_pointer_type(operand_1_type, operand_2_type); if (operation_type != NULL) { /* The types are compatible and we have computed the result-type. So we can proceed directly to the end of the function (the tests between here and there assume impl_pointer_conversion found compatibility). */ okay = TRUE; goto done; } /* if */ } /* if */ /* Go back for the second iteration with extensions allowed. */ suppress_extensions = FALSE; } /* for */ if (okay && ((operand_1_is_pointer && is_error_type(type_pointed_to(operand_1_type))) || (operand_2_is_pointer && is_error_type(type_pointed_to(operand_2_type))))) { /* One or both of the operands is a pointer to error, so don't do any further checking. */ operation_type = make_pointer_type(error_type()); goto done; } /* if */ if (okay && operand_1_is_pointer && operand_2_is_pointer && !same_entities(operand_1_type, operand_2_type)) { /* Make sure the operation type has all the cv-qualifiers present on each of the operands. */ a_type_ptr type_pointed_to_1 = type_pointed_to(operand_1_type); a_type_ptr type_pointed_to_2 = type_pointed_to(operand_2_type); a_type_ptr operation_type_pointed_to; if (same_entities(operation_type, operand_1_type)) { operation_type_pointed_to = type_plus_qualifiers_from_second_type(type_pointed_to_1, type_pointed_to_2); } else { check_assertion(same_entities(operation_type, operand_2_type)); operation_type_pointed_to = type_plus_qualifiers_from_second_type(type_pointed_to_2, type_pointed_to_1); } /* if */ operation_type = make_pointer_type(operation_type_pointed_to); } /* if */ if (okay) { a_boolean nonstd_case = FALSE; if (strict_ansi_mode && C_dialect == C_dialect_ANSI) { /* In strict ANSI C mode, issue warnings for the extensions let by above. */ if (!pointer_normalization_standard_in_C && std_conv.pointer_normalization_needed) { /* Conversion of null pointer constants to pointers, and conversion of pointers to "void *", are not standard in the present case. */ nonstd_case = TRUE; } else { a_type_ptr operand_1_type_pointed_to = NULL; a_type_ptr operand_2_type_pointed_to = NULL; /* Fetch the types pointed to by the pointer operands. */ if (operand_1_is_pointer) { operand_1_type_pointed_to = type_pointed_to(operand_1_type); operand_1_type_pointed_to = skip_typerefs(operand_1_type_pointed_to); } /* if */ if (operand_2_is_pointer) { operand_2_type_pointed_to = type_pointed_to(operand_2_type); operand_2_type_pointed_to = skip_typerefs(operand_2_type_pointed_to); } /* if */ if (!pointers_to_functions_standard_in_C && ((operand_1_is_pointer && is_function_type(operand_1_type_pointed_to)) || (operand_2_is_pointer && is_function_type(operand_2_type_pointed_to)))) { /* Pointers to functions are not standard in the present case. */ nonstd_case = TRUE; } else if (!pointers_to_incomplete_standard_in_C && ((operand_1_is_pointer && is_incomplete_type(operand_1_type_pointed_to)) || (operand_2_is_pointer && is_incomplete_type(operand_2_type_pointed_to)))) { /* Pointers to incomplete are not standard in the present case. */ nonstd_case = TRUE; } else if (!mixed_object_and_incomplete_standard_in_C && operand_1_is_pointer && operand_2_is_pointer && ((is_incomplete_type(operand_1_type_pointed_to) && is_object_type(operand_2_type_pointed_to)) || (is_incomplete_type(operand_2_type_pointed_to) && is_object_type(operand_1_type_pointed_to)))) { /* One pointer to object and one pointer to incomplete are not standard in the present case. */ nonstd_case = TRUE; } /* if */ } /* if */ if (nonstd_case) { /* A nonstandard case. */ if (expr_diagnostic_should_be_issued(strict_ansi_error_severity, ec_incompatible_operands, operator_position)) { pos_ty2_diagnostic(strict_ansi_error_severity, ec_incompatible_operands, operator_position, operand_1_type, operand_2_type); } /* if */ } /* if */ } /* if */ if (std_conv.warning_suggested != ec_no_error && !nonstd_case) { /* Oddball cases call for a warning. Suppress this if we issued a diagnostic about nonstandard use. */ if (expr_diagnostic_should_be_issued(es_warning, std_conv.warning_suggested, operator_position)) { pos_opt_ty2_warning(std_conv.warning_suggested, operator_position, operand_1_type, operand_2_type); } /* if */ } /* if */ } else { /* The operands are not compatible. */ expr_pos_ty2_error(ec_incompatible_operands, operator_position, operand_1_type, operand_2_type); operation_type = error_type(); } /* if */ done: *p_operation_type = operation_type; return okay; } /* check_compatibility_of_pointer_operands */ #if MICROSOFT_EXTENSIONS_ALLOWED a_boolean check_compatibility_of_handle_operands( an_operand *operand_1, an_operand *operand_2, a_source_position *operator_position, a_type_ptr *operation_type) /* operand_1 and operand_2 are the operands of a C++/CLI handle operation. Check to see that the operands are compatible or can be made compatible. One or the other of the operands, or both, must have a handle type. Return the operation type in *operation_type. (The operands are not cast to the operation type; the caller must do that.) operator_position gives the operator position (for errors). Return FALSE if there is an error. */ { a_boolean okay = FALSE; a_type_ptr operand_1_type = operand_1->type; a_type_ptr operand_2_type = operand_2->type; a_type_ptr local_operation_type = NULL; a_boolean operand_1_is_handle = is_handle_type(operand_1_type); a_boolean operand_2_is_handle = is_handle_type(operand_2_type); if (operand_1_is_handle) { /* See if the second operand can be converted to the type of the first operand. */ if (impl_handle_conversion(operand_2_type, operand_1_type, /*allow_qualifier_or_eh_mismatch=*/TRUE, (a_std_conv_descr *)NULL) || (operand_2->is_simple_string_literal && cli_string_literal_conversion_possible(operand_2_type, operand_1_type, (a_std_conv_descr *)NULL)) || boxing_conversion_possible(operand_2_type, operand_1_type, (a_std_conv_descr *)NULL) || /* Conversion of nullptr to a value-constrained generic is not allowed in general, but it is allowed in a handle comparison. */ (is_nullptr_type(operand_2_type) && !is_handle_type_not_value_generic(operand_1_type))) { local_operation_type = operand_1_type; okay = TRUE; } /* if */ } /* if */ if (!okay && operand_2_is_handle) { /* See if the first operand can be converted to the type of the second operand. */ if (impl_handle_conversion(operand_1_type, operand_2_type, /*allow_qualifier_or_eh_mismatch=*/TRUE, (a_std_conv_descr *)NULL) || (operand_1->is_simple_string_literal && cli_string_literal_conversion_possible(operand_1_type, operand_2_type, (a_std_conv_descr *)NULL)) || boxing_conversion_possible(operand_1_type, operand_2_type, (a_std_conv_descr *)NULL) || /* Conversion of nullptr to a value-constrained generic is not allowed in general, but it is allowed in a handle comparison. */ (is_nullptr_type(operand_1_type) && !is_handle_type_not_value_generic(operand_2_type))) { local_operation_type = operand_2_type; okay = TRUE; } /* if */ } /* if */ if (okay && ((operand_1_is_handle && is_error_type(type_pointed_to(operand_1_type))) || (operand_2_is_handle && is_error_type(type_pointed_to(operand_2_type))))) { /* One or both of the operands is a handle to error, so don't do any further checking. */ *operation_type = make_handle_type(error_type()); } else if (okay && operand_1_is_handle && operand_2_is_handle && !same_entities(operand_1_type, operand_2_type)) { /* Make sure the operation type has all the cv-qualifiers present on each of the operands. */ a_type_ptr type_pointed_to_1 = type_pointed_to(operand_1_type); a_type_ptr type_pointed_to_2 = type_pointed_to(operand_2_type); a_type_ptr operation_type_pointed_to; if (same_entities(local_operation_type, operand_1_type)) { operation_type_pointed_to = type_plus_qualifiers_from_second_type(type_pointed_to_1, type_pointed_to_2); } else { check_assertion(same_entities(local_operation_type, operand_2_type)); operation_type_pointed_to = type_plus_qualifiers_from_second_type(type_pointed_to_2, type_pointed_to_1); } /* if */ local_operation_type = make_handle_type(operation_type_pointed_to); } /* if */ if (!okay) { /* The operands are not compatible. */ expr_pos_ty2_error(ec_incompatible_operands, operator_position, operand_1_type, operand_2_type); local_operation_type = error_type(); } /* if */ *operation_type = local_operation_type; return okay; } /* check_compatibility_of_handle_operands */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ a_boolean check_compatibility_of_nullptr_operands( an_operand *operand_1, an_operand *operand_2, a_source_position *operator_position, a_type_ptr *operation_type) /* operand_1 and operand_2 are the operands of some operation, at least one of which has a nullptr type and the other has neither pointer nor pointer-to-member type nor C++/CLI handle type. (Cases involving nullptr with those types are handled by check_compatibility_of_pointer_operands, check_ptr_to_member_operands_for_compatibility, and check_compatibility_of_handle_operands, respectively.) Check to see that the operands are compatible, i.e., check for compatibility between pairs of nullptr operands or between a nullptr operand and a null pointer constant of some kind. If the operands are compatible, set *operation_type to the appropriate nullptr type; otherwise, set it to error_type(). Return FALSE if there is an error. */ { a_boolean okay = FALSE; check_assertion(!is_pointer_type(operand_1->type) && !is_pointer_type(operand_2->type) && !is_ptr_to_member_type(operand_1->type) && !is_ptr_to_member_type(operand_2->type)); #if MICROSOFT_EXTENSIONS_ALLOWED check_assertion(!is_handle_type(operand_1->type) && !is_handle_type(operand_2->type)); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (is_error_type(operand_1->type) || is_error_type(operand_2->type)) { /* An error type is compatible, but the result type will also be an error type. */ okay = TRUE; *operation_type = error_type(); } else { /* The C++/CLI (managed) nullptr is compatible only with nullptr or __nullptr; std::nullptr_t (C++/CLI __nullptr or, in other modes, nullptr) is compatible with integral null pointer constants, as well. */ if (is_managed_nullptr_type(operand_1->type)) { okay = is_nullptr_type(operand_2->type); } else if (is_managed_nullptr_type(operand_2->type)) { okay = is_nullptr_type(operand_1->type); } else if (is_nullptr_type(operand_1->type)) { okay = (is_nullptr_type(operand_2->type) || (is_constant_operand(operand_2) && is_or_might_be_null_pointer_constant(&operand_2-> variant.constant))); } else { check_assertion(is_nullptr_type(operand_2->type)); okay = is_constant_operand(operand_1) && is_or_might_be_null_pointer_constant(&operand_1-> variant.constant); } /* if */ if (okay) { /* The operation type will be a nullptr type. The Microsoft compiler follows the type of the second operand in determining whether the type will be the managed nullptr type or std::nullptr_t. */ if (is_managed_nullptr_type(operand_2->type)) { *operation_type = managed_nullptr_type(); } else { *operation_type = standard_nullptr_type(); } /* if */ } else { /* The operands are not compatible. */ expr_pos_ty2_error(ec_incompatible_operands, operator_position, operand_1->type, operand_2->type); *operation_type = error_type(); } /* if */ } /* if */ return okay; } /* check_compatibility_of_nullptr_operands */ a_boolean check_ptr_to_member_operands_for_compatibility( an_operand *operand_1, an_operand *operand_2, a_source_position *operator_position, a_type_ptr *operation_type) /* operand_1 and operand_2 are the operands of a pointer-to-member operation (==, !=, or ?:). Check to see that the operands are compatible or can be made compatible. Return the operation type in *operation_type. (The operands are not cast to the operation type; the caller must do that.) operator_position gives the operator position (for errors). Return FALSE if there is an error. */ { a_boolean okay = FALSE; a_type_ptr operand_1_type = operand_1->type; a_type_ptr operand_2_type = operand_2->type; a_std_conv_descr std_conv; /* For equality operators, relational operators, and the ?: operator (which are the only operators handled by this routine), the compatibility rules were revised through the resolution of Core issue 1512 (the C++ committee's paper N3624). The new rules apply to all C++ modes except Cfront mode. */ if (!any_cfront_mode()) { /* Start by checking the cases where an operand is a null pointer constant. If they're both null pointer constants, the operation type is nullptr_t if that type is supported; otherwise, we fall back on the rules prior to N3624. If exactly one operand is a null pointer constant, the type of the other operand is the operation type. */ if (op_is_null_pointer_constant(operand_1)) { okay = TRUE; if (op_is_null_pointer_constant(operand_2)) { unexpected_condition(); } else { okay = TRUE; *operation_type = operand_2_type; } /* if */ } else if (op_is_null_pointer_constant(operand_2)) { okay = TRUE; *operation_type = operand_1_type; } else { *operation_type = make_cv_combined_type_if_possible( operand_1_type, operand_2_type); okay = *operation_type != NULL; } /* if */ if (strict_ansi_mode || okay) { goto decided; } else { /* For nonstrict modes, try the older method if the rules of N3624 did not find a compatible operation type. */ } /* if */ } /* if */ if (is_ptr_to_member_type(operand_1_type)) { a_boolean operand_2_is_constant = is_constant_operand(operand_2); a_constant_ptr operand_2_constant = &operand_2->variant.constant; #if MICROSOFT_EXTENSIONS_ALLOWED if (microsoft_mode && !operand_2_is_constant) { /* Microsoft mode allows some expressions as null pointer constants. */ adjust_constant_operand_info_for_microsoft_null_pointer_test( operand_2, &operand_2_is_constant, &operand_2_constant, (an_expr_node **)NULL); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* See if the second operand can be converted to the type of the first operand. */ if (impl_ptr_to_member_conversion(operand_2_type, operand_2_is_constant, operand_is_function(operand_2), operand_2_constant, operand_1_type, /*allow_qualifier_or_eh_mismatch=*/TRUE, &std_conv)) { *operation_type = operand_1_type; okay = TRUE; } /* if */ } /* if */ if (!okay && is_ptr_to_member_type(operand_2_type)) { a_boolean operand_1_is_constant = is_constant_operand(operand_1); a_constant_ptr operand_1_constant = &operand_1->variant.constant; #if MICROSOFT_EXTENSIONS_ALLOWED if (microsoft_mode && !operand_1_is_constant) { /* Microsoft mode allows some expressions as null pointer constants. */ adjust_constant_operand_info_for_microsoft_null_pointer_test( operand_1, &operand_1_is_constant, &operand_1_constant, (an_expr_node **)NULL); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* See if the first operand can be converted to the type of the second operand. */ if (impl_ptr_to_member_conversion(operand_1_type, operand_1_is_constant, operand_is_function(operand_1), operand_1_constant, operand_2_type, /*allow_qualifier_or_eh_mismatch=*/TRUE, &std_conv)) { *operation_type = operand_2_type; okay = TRUE; } /* if */ } /* if */ decided: if (!okay) { /* The operands are not compatible. */ expr_pos_ty2_error(ec_incompatible_operands, operator_position, operand_1_type, operand_2_type); *operation_type = error_type(); } else if (any_cfront_mode()) { /* Cfront doesn't allow casts of pointers to members from virtual base to derived, but it does allow comparisons of such things: struct A {int j;}; struct B : public virtual A {int k;}; void f() { int A::*pa = &A::j; int B::*pb = &B::k; (void)(pa == pb); // allowed } Of course, it gets these wrong sometimes, because it doesn't do any adjustment. We allow the comparison, but we do it "right" by doing the cast in the other direction and then comparing. */ a_base_class_ptr bcp = std_conv.cast_base_class; if (bcp != NULL && !bcp->ambiguous && any_virtual_steps_in_derivation(bcp)) { if (same_entities(*operation_type, operand_1_type)) { cast_operand(operand_2_type, operand_1, /*is_implicit_cast=*/FALSE); *operation_type = operand_2_type; } else { cast_operand(operand_1_type, operand_2, /*is_implicit_cast=*/FALSE); *operation_type = operand_1_type; } /* if */ } /* if */ } /* if */ return okay; } /* check_ptr_to_member_operands_for_compatibility */ #if FIXED_POINT_ALLOWED void check_mixed_integer_fixed_point_arithmetic( an_operand *operand_1, an_operand *operand_2, an_expr_operator_kind op) /* Operator op is to be applied to the given operands. If one of the operands has a fixed-point type and the other has an integral type, issue a warning for cases that are likely to overflow (e.g., _Fract + int). */ { a_type_ptr tp1 = skip_typerefs(operand_1->type), tp2 = skip_typerefs(operand_2->type); if ((tp1->kind == (a_type_kind)tk_fixed_point && tp2->kind == (a_type_kind)tk_integer) || (tp1->kind == (a_type_kind)tk_integer && tp2->kind == (a_type_kind)tk_fixed_point)) { /* Mixed-type (integer/fixed-point) arithmetic. */ if (op == (an_expr_operator_kind)eok_add || op == (an_expr_operator_kind)eok_subtract || op == (an_expr_operator_kind)eok_add_assign || op == (an_expr_operator_kind)eok_subtract_assign) { /* Addition and subtraction involving _Fract types and integer types can easily lead to overflow because _Fract types can only hold (at most) values between -1 and +1. */ an_operand *integral_operand; a_boolean fract_type_involved; if (tp1->kind != (a_type_kind)tk_fixed_point) { integral_operand = operand_1; fract_type_involved = tp2->variant.fixed_point.is_fract_type; } else { integral_operand = operand_2; fract_type_involved = tp1->variant.fixed_point.is_fract_type; } /* if */ if (fract_type_involved) { if (op == (an_expr_operator_kind)eok_subtract && tp2->variant.fixed_point.is_unsigned && op_is_zero_constant(operand_1)) { /* Special warning on 0 - x when x is unsigned. */ expr_pos_warning(ec_unsigned_fixed_point_negation, &operand_2->position); } else if (op_is_zero_constant(operand_1) || op_is_zero_constant(operand_2)) { /* No warning on x + 0, 0 + x and similar cases. */ } else { expr_pos_warning(ec_integer_may_not_fit_in_fixed_point_result, &integral_operand->position); } /* if */ } /* if */ } /* if */ } /* if */ } /* check_mixed_integer_fixed_point_arithmetic */ static void adjust_fixed_point_binary_operands( an_operand *operand_1, an_operand *operand_2, an_expr_operator_kind op) /* A binary operation (op) is to be applied to the two given operands, at least one of which has a fixed-point type. Apply any required conversions to the operands. The only conversion this can be is from an unsigned fixed-point type to the corresponding signed fixed-point type (when operating on two fixed-point operands of differing signedness). Issue a warning if the operand types are unlikely to be intended for the given operation (e.g., adding an integer to fixed-point type.) */ { a_type_ptr tp1 = skip_typerefs(operand_1->type), tp2 = skip_typerefs(operand_2->type); if (tp1->kind == (a_type_kind)tk_fixed_point && tp2->kind == (a_type_kind)tk_fixed_point) { if (tp1->variant.fixed_point.is_unsigned != tp2->variant.fixed_point.is_unsigned) { /* One of the two operands needs to be converted to a signed type. */ an_operand *operand_to_adjust; a_type_ptr type = NULL; if (tp1->variant.fixed_point.is_unsigned) { if (is_compound_assignment_operator(op)) { /* The first operand of a compound assignment operation cannot be explicitly adjusted (the adjustment is implicit in that case). */ operand_to_adjust = NULL; } else { operand_to_adjust = operand_1; type = tp1; } /* if */ } else { operand_to_adjust = operand_2; type = tp2; } /* if */ if (operand_to_adjust != NULL) { type = fixed_point_type( make_fixed_point_type_descr( type->variant.fixed_point.precision, /*is_unsigned=*/FALSE, (a_boolean)type ->variant.fixed_point.is_fract_type, (a_boolean)type ->variant.fixed_point.saturating)); cast_operand(type, operand_to_adjust, /*is_implicit_cast=*/TRUE); } /* if */ } /* if */ } else if (!is_error_type(tp1) && !is_error_type(tp2)) { /* A mixed fixed-point/integral operation. */ check_assertion(tp1->kind == (a_type_kind)tk_fixed_point || tp2->kind == (a_type_kind)tk_fixed_point); check_mixed_integer_fixed_point_arithmetic(operand_1, operand_2, op); } /* if */ } /* adjust_fixed_point_binary_operands */ /* Macro to issue a warning for usual arithmetic conversions that end up converting a fixed-point value to a floating-point type. (Such a warning is suggested by TR 18037.) */ #define warn_on_fixed_point_to_floating_point_conversion(operand, type) \ if (fixed_point_enabled && is_fixed_point_type((operand)->type) && \ is_floating_type((type))) { \ expr_pos_warning(ec_implicit_fixed_point_to_floating_point_conversion, \ &(operand)->position); \ } /* if */ #endif /* FIXED_POINT_ALLOWED */ void change_binary_operand_types(a_type_ptr type, an_operand *operand_1, an_operand *operand_2, ARG_UNUSED an_expr_operator_kind op) /* The given operation will be applied to the given operands. If type is not a fixed-point type, cast the two operands to the new type if necessary. Otherwise (if type is a fixed-point type), ensure the operand types have the appropriate signedness (according to the rules prescribed by ISO TR 18037 for fixed-point arithmetic). This is used for the operands of an operation, with the type probably determined by determine_arithmetic_conversions. Either operand pointer may be NULL. Warnings may be issued if fixed-point operands are unlikely to have a useful effect (e.g., when adding an integer to a fixed-point operand). */ { #if FIXED_POINT_ALLOWED if (fixed_point_enabled && is_fixed_point_type(type) && op != (an_expr_operator_kind)eok_question) { /* Fixed-point arithmetic does not promote the operands to a common type if the result has fixed-point type (as opposed to floating-point type). But the "?" operator gets the usual handling of converting the operands to the result type. */ adjust_fixed_point_binary_operands(operand_1, operand_2, op); } else #endif /* FIXED_POINT_ALLOWED */ /* Do not insert code here. */ { if (!is_error_type(type)) { if (operand_1 != NULL && !cast_identical_types(operand_1->type, type)) { /* Cast operand 1 to match the desired type. */ #if FIXED_POINT_ALLOWED warn_on_fixed_point_to_floating_point_conversion(operand_1, type); #endif /* FIXED_POINT_ALLOWED */ cast_operand(type, operand_1, /*is_implicit_cast=*/TRUE); } /* if */ if (!cast_identical_types(operand_2->type, type)) { /* Cast operand 2 to match the desired type. */ #if FIXED_POINT_ALLOWED warn_on_fixed_point_to_floating_point_conversion(operand_2, type); #endif /* FIXED_POINT_ALLOWED */ cast_operand(type, operand_2, /*is_implicit_cast=*/TRUE); } /* if */ } /* if */ } /* if */ } /* change_binary_operand_types */ static a_boolean is_nonreal_member_constant(a_constant_ptr con, a_boolean *is_function) /* Return TRUE if the given constant is a nonreal member constant. Additionally, if it is for a nonreal member function, return *is_function TRUE. */ { a_boolean is_nonreal = FALSE; *is_function = FALSE; if (con->kind == (a_constant_repr_kind)ck_template_param) { if (tpck_is(con, tpck_template_ref)) { con = con->variant.template_param.variant.template_ref.con; } /* if */ if (con->variant.template_param.kind == tpck_member && /* Avoid problems with template-dependent enum constant values. */ same_entities(con->type, type_of_unknown_templ_param_nontype)) { is_nonreal = TRUE; } else if (con->variant.template_param.kind == tpck_unknown_function || con->variant.template_param.kind == tpck_destructor) { is_nonreal = TRUE; *is_function = TRUE; } /* if */ } /* if */ return is_nonreal; } /* is_nonreal_member_constant */ void change_template_param_constant_operand_to_lvalue( an_operand *operand, /* Defaulted: */ a_boolean const_qualified) /* Change the indicated operand (a prvalue for a template parameter constant) to an lvalue by placing an eok_lvalue node on top of the constant. This allows the operand (which has uncertain value category) to be used henceforth as an lvalue. If const_qualified is TRUE, the type of the operand will be const-qualified. */ { if (is_error_operand(operand)) { normalize_error_operand(operand); } else { an_expr_node_ptr expr, new_expr; a_boolean is_function; an_operand orig_operand = *operand; check_assertion(is_a_prvalue(operand) && is_constant_operand(operand)); /* Use make_node_from_operand to get operand rescan information saved. */ expr = make_node_from_operand(operand); check_assertion(!expr->is_lvalue); new_expr = make_lvalue_operator_node( eok_lvalue, (const_qualified ? make_qualified_type(expr->type, TQ_CONST) : expr->type), expr); new_expr->compiler_generated = TRUE; new_expr->position = expr->position; make_glvalue_expression_operand(new_expr, operand); if (is_nonreal_member_constant(&orig_operand.variant.constant, &is_function) && is_function) { operand->state = (an_operand_state)os_function_designator; } /* if */ restore_operand_details(operand, &orig_operand); operand->is_id_expression = orig_operand.is_id_expression; operand->id_expression_was_parenthesized = orig_operand.id_expression_was_parenthesized; } /* if */ } /* change_template_param_constant_operand_to_lvalue */ static an_expr_node_ptr conv_nonreal_member_constant_expr_to_lvalue( an_expr_node_ptr expr, a_boolean *is_function) /* Helper routine for change_nonreal_member_constant_operand_to_lvalue: Look for a prvalue for a nonreal member within the expression expr. If one is found, rewrite it as an lvalue and rewrite every node above it as an lvalue, and return a pointer to the rewritten nonreal member node. Also return *is_function set to TRUE if the node is for a nonreal member function. If no nonreal member is found, return NULL. */ { an_expr_node_ptr rewritten_expr = NULL; *is_function = FALSE; if (!expr->is_lvalue) { if (is_constant_node(expr)) { a_constant_ptr con = node_constant(expr); if (is_nonreal_member_constant(con, is_function)) { /* Add an eok_lvalue node to make the nonreal member an lvalue. */ an_expr_node_ptr new_expr = alloc_node_for_constant(con); check_assertion(!new_expr->is_lvalue); new_expr->extra.rescan_info = expr->extra.rescan_info; new_expr->variant.constant.name_reference = expr->variant.constant.name_reference; new_expr = make_lvalue_operator_node((an_expr_operator_kind)eok_lvalue, new_expr->type, new_expr); overwrite_node(expr, new_expr); rewritten_expr = expr; } /* if */ } else if (is_operation_node(expr) && (node_operator_is(expr, eok_dot_static) || node_operator_is(expr, eok_points_to_static))) { /* For static selections, do a recursive call. */ an_expr_node_ptr op1 = expr->variant.operation.operands; rewritten_expr = conv_nonreal_member_constant_expr_to_lvalue(op1->next, is_function); if (rewritten_expr != NULL) { expr->is_lvalue = TRUE; expr->type = rewritten_expr->type; } /* if */ } else if (is_operation_node(expr) && node_operator_is(expr, eok_parens)) { /* For parentheses, do a recursive call. */ an_expr_node_ptr op1 = expr->variant.operation.operands; rewritten_expr = conv_nonreal_member_constant_expr_to_lvalue(op1, is_function); if (rewritten_expr != NULL) { expr->is_lvalue = TRUE; expr->type = rewritten_expr->type; } /* if */ } /* if */ } /* if */ return rewritten_expr; } /* conv_nonreal_member_constant_expr_to_lvalue */ void change_nonreal_member_constant_operand_to_lvalue(an_operand *operand) /* If the indicated operand is a prvalue indicating the value of a member of a nonreal class, change it to an lvalue that refers to the member. Likewise for a prvalue for the address of an unknown function. The idea here is that we have a reference to a member which has unknown value category, and we have found out that it needs to be an lvalue, so we rewrite it as an lvalue. */ { an_expr_node_ptr expr = NULL; a_boolean is_function = FALSE; if (is_a_prvalue(operand)) { if (is_constant_operand(operand)) { a_constant_ptr con = &operand->variant.constant; if (is_nonreal_member_constant(con, &is_function)) { change_template_param_constant_operand_to_lvalue(operand); } else if (con->kind == (a_constant_repr_kind)ck_template_param && con->variant.template_param.kind == (a_template_param_constant_kind)tpck_expression) { expr = expr_node_from_tpck_expression(con); } /* if */ } else if (is_expression_operand(operand)) { expr = operand->variant.expression; } /* if */ } /* if */ if (expr != NULL) { /* Attempt to rewrite the underlying expression "expr". */ an_expr_node_ptr rewritten_expr; if (!expr->is_lvalue) { rewritten_expr = conv_nonreal_member_constant_expr_to_lvalue(expr, &is_function); } else { /* In some modes (Microsoft mode in particular), nonreal prvalue operands may have an underlying lvalue expression. For example: template struct A {}; template const &R> struct B { typedef B thistype; }; template const &R> struct D: B {}; Here, during a nonreal instantiation of B, the bound R in the typedef has an underlying lvalue expression (eok_ref_indirect). */ rewritten_expr = expr; } /* if */ if (rewritten_expr != NULL) { /* A nonreal member constant was found and rewritten. */ an_operand orig_operand; orig_operand = *operand; check_assertion(expr->is_lvalue && rewritten_expr->is_lvalue); make_glvalue_expression_operand(expr, operand); if (is_function) { operand->state = (an_operand_state)os_function_designator; } /* if */ restore_operand_details(operand, &orig_operand); } /* if */ } /* if */ } /* change_nonreal_member_constant_operand_to_lvalue */ void revert_class_prvalue_to_glvalue_if_possible(an_operand *operand, /* Defaulted: */ a_boolean xvalue) /* If the given operand is a class prvalue, try to change it back to a glvalue. This is used in GNU and Microsoft modes, which believe that some class prvalues can be used as glvalues (e.g., a function call returning a class by value). xvalue (which defaults to FALSE) is TRUE if the glvalue should be an xvalue. */ { if (!C_mode() && is_a_prvalue(operand) && is_class_struct_union_type(operand->type) && is_expression_operand(operand)) { an_expr_node_ptr expr = skip_parens(operand->variant.expression); a_boolean revertible = FALSE; check_assertion(!expr->is_lvalue); if (expr->kind == (an_expr_node_kind)enk_temp_init) { a_dynamic_init_ptr dip = expr->variant.init.dynamic_init; if (dip->kind == (a_dynamic_init_kind)dik_constructor) { /* A "constructor call" can be turned back into an lvalue. */ revertible = TRUE; } else if (dip->kind == (a_dynamic_init_kind)dik_expression) { /* This is probably a base-class cast on top of another temp-init. */ revertible = TRUE; } else if (dip->kind == (a_dynamic_init_kind)dik_class_result_via_ctor) { /* A function call (or statement expression) returning a class rvalue can be turned back into an lvalue. */ revertible = TRUE; } else if (dip->kind == (a_dynamic_init_kind)dik_zero) { /* A trivial constructor call with value-initialization. */ revertible = TRUE; } /* if */ } else if (is_operation_node(expr)) { if (is_call_node(expr)) { /* A function call that returns a class can be changed back into an lvalue. */ revertible = TRUE; } else if (microsoft_mode && node_operator_is(expr, eok_question)) { /* A class rvalue "?" can be turned into an lvalue in Microsoft mode. */ revertible = TRUE; } /* if */ } else if (expr->kind == (an_expr_node_kind)enk_lambda && microsoft_mode) { /* A lambda value can be turned into an lvalue. */ revertible = TRUE; } /* if */ if (revertible) { /* Change the rvalue back into an lvalue. */ conv_class_prvalue_operand_to_glvalue(operand, xvalue); } /* if */ } /* if */ } /* revert_class_prvalue_to_glvalue_if_possible */ void revert_gcc_rvalue_to_lvalue_if_possible_full( an_operand *operand, a_boolean ignore_casts, a_boolean drop_same_size_casts) /* Called only in gcc or g++ mode in a context where an lvalue is required. If operand is a prvalue that can be turned back into an lvalue, do the transformation. If ignore_casts is TRUE, casts on an lvalue do not interfere with turning it back into an lvalue (this is true, for example, in something like "(short)i = 0"). If drop_same_size_casts is TRUE, integral casts to same-sized types are dropped even when gnu_version would ordinarily indicate they should not be. */ { an_operand orig_operand; check_assertion(gnu_mode); if (is_a_prvalue(operand) && !is_void_type(operand->type)) { if (is_expression_operand(operand)) { a_boolean do_recovery = FALSE; a_boolean casts_removed = FALSE; a_type_ptr type_cast_to = NULL, type_before_cast = NULL; an_expr_node_ptr expr = skip_parens(operand->variant.expression); an_expr_operator_kind op = (an_expr_operator_kind)eok_error; a_boolean same_size_cast_case = FALSE; if (gpp_mode && gnu_version < 40000 && is_operation_node(expr) && expr->variant.operation.kind == (an_expr_operator_kind)eok_cast) { type_cast_to = expr->type; type_before_cast = expr->variant.operation.operands->type; if (identical_types(type_cast_to, type_before_cast)) { /* g++ ignores a cast to the same type. */ expr = skip_parens(expr->variant.operation.operands); } /* if */ } /* if */ if (drop_same_size_casts && is_operation_node(expr) && expr->variant.operation.kind == (an_expr_operator_kind)eok_cast) { /* See whether the top operation is a same-sized cast that should be dropped. */ type_cast_to = expr->type; type_before_cast = expr->variant.operation.operands->type; if (is_integral_or_enum_type(type_before_cast) && is_integral_or_enum_type(type_cast_to) && f_skip_typerefs(type_before_cast)->size == f_skip_typerefs(type_cast_to)->size) { same_size_cast_case = TRUE; } /* if */ } /* if */ /* expr has parentheses, if any, stripped at this point. */ if (is_operation_node(expr)) op = expr->variant.operation.kind; if ((gnu_version < 40000 || same_size_cast_case) && is_operation_node(expr) && (op == (an_expr_operator_kind)eok_cast || (gcc_mode && (op == (an_expr_operator_kind)eok_question || op == (an_expr_operator_kind)eok_comma)))) { an_expr_node_ptr orig_expr = expr; a_boolean cast_on_top_originally = (op == (an_expr_operator_kind)eok_cast && !expr->compiler_generated); /* See whether we can find an underlying lvalue. */ expr = conv_prvalue_expr_to_lvalue(expr, &do_recovery, /*see_if_possible=*/TRUE, /*gcc_lvalue=*/gcc_mode, ignore_casts, (a_type_ptr *)NULL); if (ignore_casts && do_recovery && cast_on_top_originally && expr != orig_expr) { /* One or more casts was removed. */ casts_removed = TRUE; type_cast_to = orig_expr->type; type_before_cast = expr->type; } /* if */ } else if (!C_mode() && gnu_version_is(<40600) && is_operation_node(expr) && op == (an_expr_operator_kind)eok_dot_field && !expr->variant.operation.operands->is_lvalue) { /* A field selection out of an rvalue in C++ can be converted to an lvalue selection with GCC versions prior to 4.6.x. */ an_expr_node_ptr texpr = expr->variant.operation.operands; an_expr_node_ptr texpr2 = texpr->next; a_field_ptr field = node_field(texpr2); an_operand class_operand; texpr->next = NULL; /* Turn the first operand, the class object, into an lvalue. */ make_expression_operand(texpr, &class_operand); revert_class_prvalue_to_glvalue_if_possible(&class_operand); if (is_an_lvalue(&class_operand)) { orig_operand = *operand; check_assertion(is_expression_operand(&class_operand)); texpr = class_operand.variant.expression; /* Add the field selection. */ texpr = fe_field_lvalue_selection_expr(texpr, field); make_glvalue_expression_operand(texpr, operand); restore_operand_details(operand, &orig_operand); } else { /* We failed to make an lvalue. Keep the original operation and relink the operands. */ texpr->next = texpr2; } /* if */ } else if (!C_mode() && is_class_struct_union_type(operand->type)) { /* A function call returning a class value can be treated as an lvalue. */ revert_class_prvalue_to_glvalue_if_possible(operand); } else if (gcc_mode && gnu_version < 40000 && is_variable_node(expr)) { /* In some unusual cases, like optimizing "(1 ? a : a)" to simply "a", we may have to turn a variable back into an lvalue for the variable. */ do_recovery = TRUE; } /* if */ if (do_recovery) { /* Yes, an lvalue can be recovered. */ a_type_ptr cast_type = NULL; orig_operand = *operand; if (casts_removed) { if (gcc_mode && still_an_lvalue(type_before_cast, type_cast_to)) { /* gcc treats certain "near-enough" casts as lvalue casts if it needs an lvalue. */ cast_type = type_cast_to; } /* if */ if (gpp_mode && !same_size_cast_case && (gnu_version >= 30400 || !((is_integral_type(type_cast_to) && is_integral_type(type_before_cast)) || (is_pointer_type(type_cast_to) && is_pointer_type(type_before_cast)) || (is_floating_type(type_cast_to) && is_floating_type(type_before_cast))))) { /* g++ 3.4 made this into an error. g++ versions before that gave errors on mixed cases. */ error_in_operand(ec_gcc_use_of_cast_as_lvalue, operand); } else if (cast_type == NULL) { /* Warn on an ignored cast. */ expr_pos_warning(ec_gcc_lvalue_cast_ignored, &operand->position); } else if (gnu_version >= 30400) { /* Warn on a use of an lvalue cast. gcc started warning about this in 3.4. */ expr_pos_warning(ec_gcc_use_of_cast_as_lvalue, &operand->position); } /* if */ } /* if */ if (is_error_operand(operand)) { normalize_error_operand(operand); } else { expr = conv_prvalue_expr_to_lvalue(expr, &do_recovery, /*see_if_possible=*/FALSE, /*gcc_lvalue=*/gcc_mode, ignore_casts, (a_type_ptr *)NULL); if (cast_type != NULL) { /* Adjust the result lvalue type if necessary because of a cast that's not being ignored. */ expr = make_lvalue_cast_node(expr, cast_type, /*compiler_generated=*/FALSE); } /* if */ make_glvalue_expression_operand(expr, operand); } /* if */ restore_operand_details(operand, &orig_operand); /* Restore the lvalue reference entries saved by do_cast, if any. */ operand->ref_entries_list = orig_operand.saved_ref_entries_list; } /* if */ } /* if */ } else if (is_an_lvalue(operand)) { if (is_expression_operand(operand)) { an_expr_node_ptr expr = skip_parens(operand->variant.expression); if (gpp_mode && gnu_version < 30400 && is_operation_node(expr) && expr->variant.operation.kind == (an_expr_operator_kind)eok_lvalue_cast) { /* g++ version of lvalue cast. Warn on the nonstandard use. */ expr_pos_warning(ec_gcc_use_of_cast_as_lvalue, &operand->position); } /* if */ } /* if */ } /* if */ } /* revert_gcc_rvalue_to_lvalue_if_possible_full */ void revert_gcc_rvalue_to_lvalue_if_possible(an_operand *operand, a_boolean ignore_casts) /* Called only in gcc or g++ mode in a context where an lvalue is required. If operand is an rvalue that can be turned back into an lvalue, do the transformation. See revert_gcc_rvalue_to_lvalue_if_possible_full for parameters. */ { revert_gcc_rvalue_to_lvalue_if_possible_full(operand, ignore_casts, /*drop_same_size_casts=*/FALSE); } /* revert_gcc_rvalue_to_lvalue_if_possible */ void revert_microsoft_rvalue_to_lvalue_if_possible( ARG_UNUSED an_operand *operand) /* Called in a context where an lvalue is required. If we are in Microsoft C++ mode and operand is an rvalue that can be turned back into an lvalue, do the transformation. This handles function calls that return class rvalues. */ { #if MICROSOFT_EXTENSIONS_ALLOWED if (microsoft_mode && !C_mode()) { revert_class_prvalue_to_glvalue_if_possible(operand); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* revert_microsoft_rvalue_to_lvalue_if_possible */ a_boolean check_modifiable_lvalue_operand(an_operand *operand) /* Return FALSE and issue an error message if the operand is not a modifiable lvalue. If there is an error, change the operand to an error operand. */ { a_boolean okay = FALSE; a_type_ptr type; a_boolean is_lvalue_with_complete_type; if (gnu_mode) { /* Get an lvalue back from what is ordinarily an rvalue in some cases in gcc mode. */ revert_gcc_rvalue_to_lvalue_if_possible(operand, /*ignore_casts=*/TRUE); } else if (microsoft_mode && !C_mode()) { /* Get an lvalue back from what is ordinarily an rvalue in some cases in Microsoft C++ mode. */ revert_microsoft_rvalue_to_lvalue_if_possible(operand); } /* if */ if (is_constant_operand(operand) && constant_is(&operand->variant.constant, ck_template_param) && tpck_is(&operand->variant.constant, tpck_expression)) { /* A generic expression (whose lvalueness is not necessarily known a priori). Create a generic address-of representation. */ prep_generic_operand_full(operand, /*lvalue_expected=*/TRUE, /*rvalue_expected=*/FALSE); } /* if */ /* 3.2.2.1: A modifiable lvalue has to (a) be an lvalue. (b) not have array type. (c) not have an incomplete type. (d) not have a const-qualified type. (e) if it is a struct or union, not have any const member. The check for (b) is not done, because arrays get converted to pointers to arrays in all the contexts where one wants a modifiable lvalue. */ type = operand->type; complete_type_is_needed(type); is_lvalue_with_complete_type = (is_an_lvalue(operand) && !is_incomplete_type(type)); /* In GNU, Clang, and Microsoft mode, constness is not checked if we are in a prototype instantiation.*/ if (is_lvalue_with_complete_type && (!is_const_qualified_type(type) || ((gpp_version_is(<140000) || clang_version_is(<100000) || microsoft_mode) && is_prototype_instantiation_or_expansion_context()))) { #if MICROSOFT_EXTENSIONS_ALLOWED a_boolean is_static_initonly_field; if (cli_or_cx_enabled && is_unmodifiable_initonly_field_operand(operand, &is_static_initonly_field)) { /* A C++/CLI initonly field can be modified only in an appropriate constructor of its own class. */ error_in_operand(is_static_initonly_field ? ec_modification_of_static_initonly_field : ec_modification_of_initonly_field, operand); } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here. */ { /* In SVR4 C compatibility mode, this routine can be called for an lvalue cast that would normally be illegal. Issue a warning. */ if (SVR4_C_mode && is_expression_operand(operand)) { an_expr_node_ptr expr = skip_parens(operand->variant.expression); if (is_operation_node(expr) && node_operator_is(expr, eok_lvalue_cast)) { expr_pos_warning(ec_expr_not_a_modifiable_lvalue, &operand->position); } /* if */ } /* if */ okay = TRUE; if (is_class_struct_union_type(type)) { type = skip_typerefs(type); if (type->variant.class_struct_union.any_const_member) okay = FALSE; } /* if */ } /* if */ } /* if */ if (!okay) { if (is_error_operand(operand)) { /* An error message has already been issued for this operand. */ } else if (gcc_mode && is_lvalue_with_complete_type) { /* This is a discretionary error in gcc mode. */ expr_pos_diagnostic(es_discretionary_error, ec_expr_not_a_modifiable_lvalue, &operand->position); okay = TRUE; } else { error_in_operand(ec_expr_not_a_modifiable_lvalue, operand); } /* if */ } /* if */ return okay; } /* check_modifiable_lvalue_operand */ a_boolean construct_not_allowed_in_cpp11_constant_expr( an_error_code err_code, a_source_position *pos) /* Called from expression-scanning routines when the construct is not allowed in a C++11 constant expression. The indicated error is issued at the indicated position, and TRUE is returned, if we are currently inside a constant expression (and the expression is evaluated). */ { a_boolean err = FALSE; /* Constant expressions allow invalid operators/constructs in unevaluated subexpressions, including dead operands of "?", "&&", and "||". */ if (constexpr_enabled && curr_expr_is_evaluated()) { expr_stack->constant_expr_ruled_out = TRUE; if (curr_expr_kind_is_const() && !expr_stack->inside_conditional_expression) { /* We're in a constant expression, so this construct is an error. */ expr_pos_error(err_code, pos); err = TRUE; } /* if */ } /* if */ return err; } /* construct_not_allowed_in_cpp11_constant_expr */ a_boolean operator_not_allowed_in_cpp11_constant_expr(a_source_position *pos) /* Called from operator-scanning routines when the operator is not allowed in a C++11 constant expression. An error is issued at the indicated position, and TRUE is returned, if we are currently inside a constant expression (and the expression is evaluated). */ { a_boolean err = !relaxed_constexpr_allowed() && construct_not_allowed_in_cpp11_constant_expr( ec_bad_constant_operator, pos); return err; } /* operator_not_allowed_in_cpp11_constant_expr */ an_error_code expr_not_integral_or_any_enum_code(void) /* Return the appropriate diagnostic code to indicate that an expression is not an integer or an enum (both scoped and unscoped enum types are acceptable here). In C mode, it is sufficient to say that is doesn't have an "integral" type". In C++ mode, enum types are not "integral types" but they are acceptable in this context and hence the diagnostic should make that clear. If scoped enum types should be excluded, use expr_not_integral_code instead. */ { an_error_code result; if (enum_type_is_integral) { result = ec_expr_not_integral; } else { result = ec_expr_not_integral_or_enum; } /* if */ return result; } /* expr_not_integral_or_any_enum_code */ an_error_code expr_not_integral_code(void) /* Return the appropriate diagnostic code to indicate that an expression is not an integer or an unscoped enum. In C mode, it is sufficient to say that is doesn't have an "integral" type". In C++ mode, unscoped enum types are not "integral types" but they are acceptable in this context and hence the diagnostic should make that clear. In traditional C++, there is no distinction between "scoped" and "unscoped" enums, so we use a diagnostic that doesn't mention that distinction. */ { an_error_code result; if (enum_type_is_integral) { result = ec_expr_not_integral; } else if (cpp11_mode || cli_or_cx_enabled) { /* Modes that make a distinction between scoped and unscoped enum types. */ result = ec_expr_not_integral_or_unscoped_enum; } else { result = ec_expr_not_integral_or_enum; } /* if */ return result; } /* expr_not_integral_code */ a_boolean check_integral_or_enum_operand(an_operand *operand) /* Return FALSE and issue an error message if the operand is not of integral type (including unscoped enum types, but not scoped enum types). If there is an error change "operand" to an error operand. */ { a_boolean okay = TRUE; if (is_error_operand(operand)) { /* If the operand has a type of error, an error message has already been issued. */ okay = FALSE; } else if (!is_integral_or_unscoped_enum_type(operand->type)) { /* Use different diagnostics depending on the mode. */ error_in_operand(expr_not_integral_code(), operand); okay = FALSE; } /* if */ return okay; } /* check_integral_or_enum_operand */ a_boolean check_integral_or_enum_or_fixed_point_operand(an_operand *operand) /* Return FALSE and issue an error message if the operand is not of integral, unscoped enumeration, or fixed-point type (scoped enumeration types are not accepted). If there is an error change "operand" to an error operand. */ { a_boolean okay = TRUE; if (is_error_operand(operand)) { /* If the operand has a type of error, an error message has already been issued. */ okay = FALSE; #if FIXED_POINT_ALLOWED } else if (is_fixed_point_type(operand->type)) { /* Fixed-point types are acceptable. */ #endif /* FIXED_POINT_ALLOWED */ } else if (!is_integral_or_unscoped_enum_type(operand->type)) { an_error_code error_code; /* Use different diagnostics depending on the mode. */ if (fixed_point_enabled) { if (enum_type_is_integral) { error_code = ec_expr_not_integral_or_fixed_point; } else if (cpp11_mode || cli_or_cx_enabled) { error_code = ec_expr_not_integral_or_unscoped_enum_or_fixed_point; } else { error_code = ec_expr_not_integral_or_enum_or_fixed_point; } /* if */ } else { error_code = expr_not_integral_code(); } /* if */ error_in_operand(error_code, operand); okay = FALSE; } /* if */ return okay; } /* check_integral_or_enum_or_fixed_point_operand */ an_error_code expr_not_arithmetic_code(void) /* Return the appropriate diagnostic code to indicate that an expression is not "arithmetic". In C mode, it is sufficient to say that is doesn't have an "arithmetic type". In C++ mode, unscoped enum types are not "arithmetic types" but they are acceptable in this context and hence the diagnostic should make that clear. In traditional C++, there is no distinction between "scoped" and "unscoped" enums, so we use a diagnostic that doesn't mention that distinction. */ { an_error_code result; if (enum_type_is_integral) { result = ec_expr_not_arithmetic; } else if (cpp11_mode || cli_or_cx_enabled) { /* Modes that make a distinction between scoped and unscoped enum types. */ result = ec_expr_not_arithmetic_or_unscoped_enum; } else { result = ec_expr_not_arithmetic_or_enum; } /* if */ return result; } /* expr_not_arithmetic_code */ static an_error_code expr_not_arithmetic_or_pointer_code(void) /* Return the appropriate diagnostic code to indicate that an expression is neither "arithmetic" nor a pointer. In C mode, it is sufficient to say that is doesn't have a "scalar type". In C++ mode, unscoped enum types are not "arithmetic types" but they are acceptable in this context and hence the diagnostic should make that clear. (C++ cannot use the "scalar type" term in this context because pointer-to-member types are scalar but not acceptable in this context.) In traditional C++, there is no distinction between "scoped" and "unscoped" enums, so we use a diagnostic that doesn't mention that distinction. */ { an_error_code result; if (enum_type_is_integral) { result = ec_expr_not_scalar; } else if (cpp11_mode || cli_or_cx_enabled) { /* Modes that make a distinction between scoped and unscoped enum types. */ result = ec_expr_not_arithmetic_or_unscoped_enum_or_pointer; } else { result = ec_expr_not_arithmetic_or_enum_or_pointer; } /* if */ return result; } /* expr_not_arithmetic_or_pointer_code */ an_error_code type_not_arithmetic_or_pointer_code(void) /* This function is nearly identical to expr_not_arithmetic_or_pointer_code, except that the error code takes a type fill-in to report the type that doesn't meet expectations. */ { an_error_code result; if (enum_type_is_integral) { result = ec_type_not_scalar; } else if (cpp11_mode || cli_or_cx_enabled) { /* Modes that make a distinction between scoped and unscoped enum types. */ result = ec_type_not_arithmetic_or_unscoped_enum_or_pointer; } else { result = ec_type_not_arithmetic_or_enum_or_pointer; } /* if */ return result; } /* type_not_arithmetic_or_pointer_code */ a_boolean check_arithmetic_or_enum_operand(an_operand *operand) /* Return FALSE if the operand is not of arithmetic type (including unscoped enum types). If there is an error, change the operand to an error operand. */ { a_boolean okay = TRUE; if (is_error_operand(operand)) { /* If it is an error type, an error message has already been issued. */ okay = FALSE; } else if (!is_arithmetic_or_unscoped_enum_type(operand->type)) { error_in_operand(expr_not_arithmetic_code(), operand); okay = FALSE; } /* if */ return okay; } /* check_arithmetic_or_enum_operand */ a_boolean check_pointer_operand(an_operand *operand, an_error_code err_code) /* Return FALSE and issue an error message if the operand is not a pointer type. If there is an error, make "operand" into an error operand. */ { a_boolean okay = TRUE; /* Catch an array rvalue in pre-C99 C. This is nonstandard. Convert it to a pointer to the first element of the array. */ handle_nonstandard_array_rvalue(operand); if (is_error_operand(operand)) { /* If it is an error operand, an error message has already been issued. */ okay = FALSE; } else if (!is_pointer_type(operand->type)) { type_error_in_operand(err_code, operand, operand->type); okay = FALSE; } /* if */ return okay; } /* check_pointer_operand */ #if MICROSOFT_EXTENSIONS_ALLOWED a_boolean check_pointer_or_handle_operand(an_operand *operand, an_error_code err_code) /* Return FALSE and issue an error message if the operand is neither a pointer type nor a C++/CLI handle type (this check should be called only if cli_or_cx_enabled is TRUE). If there is an error, make "operand" into an error operand. */ { a_boolean okay = TRUE; check_assertion(cli_or_cx_enabled); if (is_error_operand(operand)) { /* If it is an error operand, an error message has already been issued. */ okay = FALSE; } else if (!is_pointer_type(operand->type) && !is_handle_type(operand->type)) { type_error_in_operand(err_code, operand, operand->type); okay = FALSE; } /* if */ return okay; } /* check_pointer_or_handle_operand */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ a_boolean check_object_pointer_operand(an_operand *operand, an_error_code err_code) /* Return FALSE and issue an error message if the operand is not a pointer to object type (more precisely, for C++, a pointer to a complete object type). If there is an error, change "operand" to an error operand. */ { a_boolean okay = TRUE; a_type_ptr underlying_type; if (!check_pointer_operand(operand, err_code)) { okay = FALSE; } else { /* Instantiate the underlying type if it is a template class. */ underlying_type = type_pointed_to(operand->type); complete_type_is_needed(underlying_type); if (!is_complete_object_type(underlying_type)) { if (gpp_version_is(any_version) && is_template_dependent_context() && !scope_stack_top().is_rescan) { pos_warning(ec_expr_not_object_pointer, &operand->position); generic_cast_operand(operand, type_of_unknown_templ_param_nontype, csf_none, /*is_implicit_cast=*/TRUE); } else { error_in_operand(ec_expr_not_object_pointer, operand); okay = FALSE; } /* if */ } else if (is_sizeless_type(underlying_type)) { type_error_in_operand(ec_sizeless_type_not_allowed, operand, underlying_type); okay = FALSE; } /* if */ } /* if */ return okay; } /* check_object_pointer_operand */ #if PTR_TO_INCOMP_ARRAY_ARITHMETIC_ALLOWED a_boolean check_object_or_incomp_array_pointer_operand(an_operand *operand, an_error_code err_code, an_operand *otherop) /* Return FALSE and issue an error message if the operand is not a pointer to complete object or incomplete array. If there is an error, change "operand" to an error operand. If the operand is a pointer to incomplete array, issue a remark if otherop is a constant zero, a warning otherwise. Do not accept the pointer to incomplete array case in strict ANSI mode. */ { a_boolean okay = TRUE; a_type_ptr underlying_type; if (!check_pointer_operand(operand, err_code)) { okay = FALSE; } else { underlying_type = type_pointed_to(operand->type); /* Instantiate the underlying type if it is a template class. */ complete_type_is_needed(underlying_type); if (is_complete_object_type(underlying_type)) { if (is_sizeless_type(underlying_type)) { type_error_in_operand(ec_sizeless_type_not_allowed, operand, underlying_type); okay = FALSE; } else { /* okay = TRUE; -- Already set. */ } /* if */ } else if ((!strict_ansi_mode || strict_ansi_error_severity == es_warning) && is_array_type(underlying_type) && is_incomplete_type(underlying_type)) { /* Pointer to incomplete array. If the other operand (the one being combined with the pointer) is a constant zero, issue a remark (that's a case like p[0]); otherwise, issue a warning. */ if (op_is_zero_constant(otherop) && !strict_ansi_mode) { if (expr_diagnostic_should_be_issued(es_remark, err_code, &operand->position)) { expr_pos_ty_diagnostic(es_remark, err_code, &operand->position, operand->type); } /* if */ } else { expr_pos_ty_diagnostic(es_warning, err_code, &operand->position, operand->type); } /* if */ } else { /* A pointer, but not a valid pointer. */ type_error_in_operand(err_code, operand, operand->type); okay = FALSE; } /* if */ } /* if */ return okay; } /* check_object_or_incomp_array_pointer_operand */ #endif /* PTR_TO_INCOMP_ARRAY_ARITHMETIC_ALLOWED */ a_boolean check_call_function_pointer_operand(an_operand *operand) /* The given operand appears preceding the opening parenthesis of an apparent call. Return FALSE and issue an error message if it is not a pointer to a function type. If there is an error, change the operand to an error operand. */ { a_boolean okay = TRUE; a_type_ptr pointer_type; if (is_error_operand(operand)) { /* If the operand has a type of error, an error message has already been issued. */ okay = FALSE; } else { pointer_type = operand->type; if (!is_pointer_type(pointer_type) || !is_function_type(type_pointed_to(pointer_type))) { error_in_operand(ec_expr_not_ptr_to_function, operand); okay = FALSE; } /* if */ } /* if */ return okay; } /* check_call_function_pointer_operand */ a_boolean check_scalar_operand(an_operand *operand) /* Return FALSE and issue an error message if the operand is not of a simple scalar type. If there is an error, change "operand" to an error operand. */ { a_boolean okay = TRUE; if (is_error_operand(operand)) { /* If the operand has a type of error, an error message has already been issued. */ okay = FALSE; } else if (!is_simple_scalar_type(operand->type)) { error_in_operand(expr_not_arithmetic_or_pointer_code(), operand); okay = FALSE; } /* if */ return okay; } /* check_scalar_operand */ a_boolean operand_is_lvalue_for_variable(an_operand *operand, a_variable_ptr *var) /* If the given operand represents an lvalue for a variable, return TRUE and make *var point to the IL entry for that variable. Otherwise, return FALSE. A variable surrounded by parentheses still counts as the simple variable. */ { a_boolean result = FALSE; *var = NULL; if (is_expression_operand(operand)) { an_expr_node_ptr expr = skip_parens(operand->variant.expression); if (is_variable_node(expr) && expr->is_lvalue) { result = TRUE; *var = node_variable(expr); } /* if */ } /* if */ return result; } /* operand_is_lvalue_for_variable */ a_boolean operand_is_lvalue_for_rref_variable(an_operand *operand, a_variable_ptr *var) /* If the given operand represents an lvalue for an rvalue reference variable, return TRUE and make *var point to the IL entry for that variable. Otherwise, return FALSE. A variable surrounded by parentheses still counts as the simple variable. */ { a_boolean result = FALSE; *var = NULL; if (is_expression_operand(operand)) { an_expr_node_ptr expr = skip_parens(operand->variant.expression); if (expr->is_lvalue && is_operation_node(expr) && node_operator_is(expr, eok_ref_indirect)) { expr = expr->variant.operation.operands; if (is_variable_node(expr) && is_rvalue_reference_type(node_variable(expr)->type)) { result = TRUE; *var = node_variable(expr); } /* if */ } /* if */ } /* if */ return result; } /* operand_is_lvalue_for_rref_variable */ #if GNU_EXTENSIONS_ALLOWED static a_boolean operand_is_prvalue_for_variable(an_operand *operand, a_variable_ptr *var) /* If the given operand represents an prvalue for a variable, return TRUE and make *var point to the IL entry for that variable. Otherwise, return FALSE. A variable surrounded by parentheses still counts as the simple variable. */ { a_boolean result = FALSE; *var = NULL; if (is_expression_operand(operand)) { an_expr_node_ptr expr = skip_parens(operand->variant.expression); if (is_variable_node(expr) && !expr->is_lvalue && !expr->is_xvalue) { result = TRUE; *var = node_variable(expr); } /* if */ } /* if */ return result; } /* operand_is_prvalue_for_variable */ #endif /* GNU_EXTENSIONS_ALLOWED */ a_boolean op_is_zero_constant(an_operand *operand) /* Return TRUE if the operand contains a constant zero value (of integral or floating type). */ { a_boolean is_constant_zero = FALSE; an_expr_node_ptr node; if (is_expression_operand(operand)) { /* Check if the expression is a constant zero. */ node = skip_parens(operand->variant.expression); if (is_constant_node(node) && is_zero_constant(node_constant(node))) { is_constant_zero = TRUE; } /* if */ } else if (is_constant_operand(operand)) { /* Check the constant in the operand itself. */ is_constant_zero = is_zero_constant(&operand->variant.constant); } /* if */ return is_constant_zero; } /* op_is_zero_constant */ a_boolean op_is_false_constant(an_operand *operand) /* Return TRUE if the operand contains a false constant value (a zero of integral, floating, pointer, or pointer to member type). Note that it may be necessary to call constant_bool_value_known_at_compile_time to rule out certain cases before calling this routine. */ { a_boolean is_constant_false = FALSE; an_expr_node_ptr node; if (is_expression_operand(operand)) { /* Check if the expression is a false constant. */ node = skip_parens(operand->variant.expression); if (is_constant_node(node) && is_false_constant(node_constant(node))) { is_constant_false = TRUE; } /* if */ } else if (is_constant_operand(operand)) { /* Check the constant in the operand itself. */ is_constant_false = is_false_constant(&operand->variant.constant); } /* if */ return is_constant_false; } /* op_is_false_constant */ a_boolean op_is_null_pointer_value(an_operand *operand) /* Return TRUE if the given operand represents a null pointer value. Note that is "null pointer value" not "null pointer constant". */ { a_boolean is_null = FALSE; if (is_a_prvalue(operand) && is_constant_operand(operand)) { is_null = is_null_pointer_value(&operand->variant.constant); } /* if */ return is_null; } /* op_is_null_pointer_value */ a_boolean op_is_null_address_lvalue(an_operand *operand) /* Return TRUE if the given operand is an lvalue whose address is a null pointer value. */ { a_boolean is_null = FALSE; if (is_an_lvalue(operand) && !operand->is_dummy_lvalue) { a_constant_ptr con = local_constant(); an_expr_node_ptr expr = extract_node_from_operand(operand, /*no_rewrite=*/TRUE); if (constant_glvalue_address(expr, con, /*address_escapes=*/FALSE)) { if (is_null_pointer_value(con)) { is_null = TRUE; } /* if */ } /* if */ release_local_constant(&con); } /* if */ return is_null; } /* op_is_null_address_lvalue */ a_boolean pointer_operand_cannot_be_null(an_operand *operand) /* Return TRUE if the indicated operand is a prvalue pointer operand that is known not to have a NULL value. The prediction doesn't have to be perfect; the safe value is FALSE. */ { a_boolean op_cannot_be_null = FALSE; if (is_a_prvalue(operand) && is_pointer_type(operand->type)) { if (is_constant_operand(operand)) { a_constant_ptr con = &operand->variant.constant; if (constant_bool_value_known_at_compile_time(con) && !is_null_pointer_value(con)) { op_cannot_be_null = TRUE; } /* if */ } else if (is_expression_operand(operand)) { an_expr_node_ptr expr = operand->variant.expression; if (cannot_be_null(expr)) { op_cannot_be_null = TRUE; } /* if */ } /* if */ } /* if */ return op_cannot_be_null; } /* pointer_operand_cannot_be_null */ static a_boolean constant_is_pointer_to_array_variable( a_constant_ptr con, a_type_ptr *array_type) /* Return TRUE if the indicated constant is the address of an array variable after array-to-pointer decay, possibly at a level after the first if the array is multi-dimensional. Return the array type in *array_type. */ { a_boolean result = FALSE; *array_type = NULL; if (con->kind == (a_constant_repr_kind)ck_address && con->variant.address.kind == (an_address_base_kind)abk_variable && con->implicit_cast && is_pointer_type(con->type)) { a_type_ptr var_type = con->variant.address.variant.variable->type; a_type_ptr con_underlying_type = type_pointed_to(con->type); a_targ_size_t var_size = skip_typerefs(var_type)->size; while (is_array_type(var_type)) { a_type_ptr element_type = array_element_type(var_type); if (identical_types(element_type, con_underlying_type)) { /* The constant type matches one of the levels of array in the variable. See if the entire array fits at the indicated offset in the variable. */ element_type = skip_typerefs(element_type); if (con->variant.address.offset >= 0 && (sizeof_t)con->variant.address.offset < var_size) { if (element_type->size <= (var_size - (a_targ_size_t)con->variant.address.offset)) { *array_type = var_type; result = TRUE; } /* if */ } /* if */ break; } /* if */ var_type = element_type; } /* while */ } /* if */ return result; } /* constant_is_pointer_to_array_variable */ static a_boolean valid_node_if_subscript(an_expr_node_ptr node, a_boolean *just_past_end, an_error_code *err_code) /* Check the given node to see if it represents a subscript operation with a constant subscript. If so, check the subscript and return FALSE if it's not valid. Return *just_past_end TRUE if the subscript is just past the end of the array (which is legal when the address is used, but not when the value is used). The node can be an lvalue or an rvalue. Pointer additions and subtractions are also checked (e.g., a + 5, where 5 is beyond the end of the array a). This routine checks these operations when the pointer operand is not constant; when it is, the folding routines (e.g., do_padd) do the check. It is important that the check be done only once, so that a warning is issued only once. Therefore, it is done immediately when the operator is scanned, either in the constant folding or here. Subscript operations are always lvalues initially, and therefore not constant, and are therefore always checked here. When a constant addressing expression is folded to a constant later, no warning is generated for an invalid subscript or pointer operation, on the assumption that the warning was already generated when the operator was scanned. *err_code is set to the error code for an appropriate warning (the pointer +/- cases use a different message than the subscript case). */ { a_boolean valid = TRUE; an_expr_node_ptr ptr_node, sub_node; a_type_ptr ptr_type, underlying_type, array_type, element_type; a_type_ptr ptr_element_type; a_constant_ptr sub_con; a_targ_size_t num_elements; int cmp; a_boolean prev_subsc_just_past_end = FALSE; *just_past_end = FALSE; *err_code = ec_no_error; node = skip_parens(node); if (is_operation_node(node) && node_operator_is(node, eok_indirect)) { ptr_node = skip_parens(node->variant.operation.operands); if (is_operation_node(ptr_node)) { if (node_operator_is(ptr_node, eok_array_to_pointer)) { /* Indirection over an array-decay is effectively a [0] subscript at the end, i.e., *a[3] is the same as a[3][0] if a is a two-dimensional array. Since the final effective subscript is [0], do the subscript analysis on the previous subscript, and its validity and just-past-end flag will be correct for this level as well. */ node = skip_parens(ptr_node->variant.operation.operands); } else if (node_operator_is(ptr_node, eok_padd)) { /* Indirection over a pointer "+" is equivalent to subscripting. */ node = ptr_node; } else if (node_operator_is(ptr_node, eok_psubtract)) { /* Indirection over a pointer "-" is equivalent to subscripting, with the integer operand negated. */ node = ptr_node; } /* if */ } /* if */ } /* if */ if (curr_expr_is_evaluated() && is_operation_node(node) && (node_operator_is(node, eok_padd) || node_operator_is(node, eok_psubtract) || node_operator_is(node, eok_subscript))) { /* The node is a pointer addition or subscript operation. */ ptr_node = node->variant.operation.operands; sub_node = ptr_node->next; if (sub_node != NULL && sub_node->next == NULL) { if (is_pointer_type(sub_node->type)) { /* The pointer is the second operand; logically swap the operands. */ sub_node = ptr_node; ptr_node = sub_node->next; } /* if */ ptr_node = skip_parens(ptr_node); sub_node = skip_parens(sub_node); if (is_constant_node(sub_node)) { sub_con = node_constant(sub_node); if (constant_is(sub_con, ck_integer)) { a_targ_ptrdiff_t offset = 0; /* The subscript is an integer constant. The other operand should have type "pointer to x" (the test rules out error cases). */ ptr_type = ptr_node->type; if (is_pointer_type(ptr_type)) { /* See if we can find the array type "array of x" underneath that. */ underlying_type = NULL; if (is_constant_node(ptr_node)) { a_constant_ptr con = node_constant(ptr_node); a_constant_ptr scon; if (constant_is_pointer_to_string_literal(con, &scon)) { /* The constant is a string literal decayed or cast to a pointer type. */ underlying_type = scon->type; } else if (constant_is_pointer_to_array_variable( con, &underlying_type)) { /* The constant is the address of a variable. */ offset = con->variant.address.offset; if (offset != 0) { if (!is_array_type(underlying_type)) { /* The code below does not currently handle cases where we start with the address of an element that is not at the start of an array. */ underlying_type = NULL; } else { a_targ_size_t elem_size = skip_typerefs(array_element_type(underlying_type))->size; check_assertion(elem_size != 0); offset /= (a_targ_ptrdiff_t)elem_size; } /* if */ } /* if */ } /* if */ } else if (is_operation_node(ptr_node)) { /* We can get the array if the top operation on the pointer operand is an array-to-pointer decay. */ if (node_operator_is(ptr_node, eok_array_to_pointer)) { an_error_code local_err_code; ptr_node = ptr_node->variant.operation.operands; underlying_type = ptr_node->type; /* Check for a subscript at the next level down (i.e., we have multi-dimensional subscripting). Only at the end of a string of subscripts can we check the validity of just-past-the-end subscripts on the earlier subscripts. */ (void)valid_node_if_subscript(ptr_node, &prev_subsc_just_past_end, &local_err_code); } /* if */ } /* if */ if (underlying_type != NULL) { /* See if the underlying_type is an array type. */ /* Note that is_array_type returns TRUE for incomplete array types. We can only check the subscript if the array type is complete. */ if (is_array_type(underlying_type) && !is_incomplete_type(underlying_type)) { array_type = skip_typerefs(underlying_type); /* See if the element type of the array type matches the type pointed to by ptr_type. */ ptr_element_type = type_pointed_to(ptr_type); ptr_element_type = skip_typerefs(ptr_element_type); element_type = array_element_type(array_type); element_type = skip_typerefs(element_type); if (identical_types(ptr_element_type, element_type)) { /* Everything's as we want it. Check the subscript. */ a_boolean err; an_integer_value offset_int; a_constant_ptr eff_sub_con = sub_con; a_constant_ptr local_con = local_constant(); if (node_operator_is(node, eok_subscript)) { *err_code = ec_subscript_out_of_range; } else { *err_code = ec_pointer_outside_base_object; } /* if */ /* For the pointer "-" case negate the constant. */ if (node_operator_is(node, eok_psubtract)) { if (!int_constant_is_signed(sub_con)) { goto invalid_subscript; } /* if */ *local_con = *sub_con; eff_sub_con = local_con; negate_integer_value(&local_con->variant.integer_value, &err); if (err) goto invalid_subscript; } /* if */ set_integer_value(&offset_int, offset); add_integer_values(&offset_int, &eff_sub_con->variant.integer_value, /*is_signed=*/TRUE, &err); if (sign_of(offset_int)) { /* Negative subscript (positive for pointer "-"). */ invalid_subscript: valid = FALSE; } else if (prev_subsc_just_past_end) { /* A previous subscript was just past the end. */ if (sign_of_integer_constant(eff_sub_con) == 0) { /* This one is zero, so we're still just at the end. */ *just_past_end = TRUE; } else { /* This one is positive, so we're off the end. */ valid = FALSE; } /* if */ } else if (vla_enabled && is_vla_type(underlying_type)) { /* Variable-length arrays cannot be checked for non-negative subscripts. */ #if UPC_EXTENSIONS_ALLOWED } else if (array_type->variant.array.is_threads_dimension) { /* Do not check subscripts on threads-dimensioned arrays. */ #endif /* UPC_EXTENSIONS_ALLOWED */ } else if (array_type ->variant.array.is_template_dependent_size_array) { /* Can't check template-dependent-sized arrays. */ } else { check_assertion(!has_unknown_specified_bound(array_type)); num_elements = array_type-> variant.array.variant.number_of_elements; /* Do not check subscripts on arrays dimensioned as having size 1, since that's probably a clue that the programmer is cheating. */ if (num_elements > 1) { cmp = cmpulit_integer_constant(eff_sub_con, (a_host_large_unsigned)num_elements); valid = (cmp <= 0); /* Subscript <= number of elements */ *just_past_end = (cmp == 0); /* Subscript == number of elements */ } /* if */ } /* if */ release_local_constant(&local_con); } /* if */ } /* if */ } /* if */ } /* if */ } /* if */ } /* if */ } /* if */ } /* if */ return valid; } /* valid_node_if_subscript */ an_expr_operator_kind which_binary_operator(a_token_kind token, a_type_ptr type) /* Return a binary expression operator based on the token and type. If the type is an error type, return eok_error. */ { an_expr_operator_kind op = (an_expr_operator_kind)eok_error; a_type_kind type_kind = skip_typerefs(type)->kind; switch (token) { case tok_plus: #if C99_IL_EXTENSIONS_SUPPORTED check_assertion(type_kind != (a_type_kind)tk_imaginary); #endif /* C99_IL_EXTENSIONS_SUPPORTED */ op = (an_expr_operator_kind)(type_kind == (a_type_kind)tk_pointer ? eok_padd : eok_add); break; case tok_minus: #if C99_IL_EXTENSIONS_SUPPORTED check_assertion(type_kind != (a_type_kind)tk_imaginary); #endif /* C99_IL_EXTENSIONS_SUPPORTED */ op = (an_expr_operator_kind)(type_kind == (a_type_kind)tk_pointer ? eok_psubtract : eok_subtract); break; case tok_star: #if C99_IL_EXTENSIONS_SUPPORTED check_assertion(type_kind != (a_type_kind)tk_imaginary); #endif /* C99_IL_EXTENSIONS_SUPPORTED */ op = (an_expr_operator_kind)eok_multiply; break; case tok_divide: #if C99_IL_EXTENSIONS_SUPPORTED check_assertion(type_kind != (a_type_kind)tk_imaginary); #endif /* C99_IL_EXTENSIONS_SUPPORTED */ op = (an_expr_operator_kind)eok_divide; break; case tok_remainder: op = (an_expr_operator_kind)eok_remainder; break; case tok_shift_right: op = (an_expr_operator_kind)eok_shiftr; break; case tok_shift_left: op = (an_expr_operator_kind)eok_shiftl; break; case tok_lt: #if GNU_VECTOR_TYPES_ALLOWED if (type_kind == (a_type_kind)tk_vector) { op = (an_expr_operator_kind)eok_vector_lt; } else #endif /* GNU_VECTOR_TYPES_ALLOWED */ /* Do not insert code here. */ { op = (an_expr_operator_kind)eok_lt; } /* if */ break; case tok_gt: #if GNU_VECTOR_TYPES_ALLOWED if (type_kind == (a_type_kind)tk_vector) { op = (an_expr_operator_kind)eok_vector_gt; } else #endif /* GNU_VECTOR_TYPES_ALLOWED */ /* Do not insert code here. */ { op = (an_expr_operator_kind)eok_gt; } /* if */ break; case tok_le: #if GNU_VECTOR_TYPES_ALLOWED if (type_kind == (a_type_kind)tk_vector) { op = (an_expr_operator_kind)eok_vector_le; } else #endif /* GNU_VECTOR_TYPES_ALLOWED */ /* Do not insert code here. */ { op = (an_expr_operator_kind)eok_le; } /* if */ break; case tok_ge: #if GNU_VECTOR_TYPES_ALLOWED if (type_kind == (a_type_kind)tk_vector) { op = (an_expr_operator_kind)eok_vector_ge; } else #endif /* GNU_VECTOR_TYPES_ALLOWED */ /* Do not insert code here. */ { op = (an_expr_operator_kind)eok_ge; } /* if */ break; case tok_eq: #if GNU_VECTOR_TYPES_ALLOWED if (type_kind == (a_type_kind)tk_vector) { op = (an_expr_operator_kind)eok_vector_eq; } else #endif /* GNU_VECTOR_TYPES_ALLOWED */ /* Do not insert code here. */ { op = (an_expr_operator_kind)eok_eq; } /* if */ break; case tok_ne: #if GNU_VECTOR_TYPES_ALLOWED if (type_kind == (a_type_kind)tk_vector) { op = (an_expr_operator_kind)eok_vector_ne; } else #endif /* GNU_VECTOR_TYPES_ALLOWED */ /* Do not insert code here. */ { op = (an_expr_operator_kind)eok_ne; } /* if */ break; case tok_spaceship: op = (an_expr_operator_kind)eok_spaceship; break; case tok_ampersand: op = (an_expr_operator_kind)eok_and; break; case tok_excl_or: op = (an_expr_operator_kind)eok_xor; break; case tok_or: op = (an_expr_operator_kind)eok_or; break; case tok_and_and: #if GNU_VECTOR_TYPES_ALLOWED if (type_kind == (a_type_kind)tk_vector) { op = (an_expr_operator_kind)eok_vector_land; } else #endif /* GNU_VECTOR_TYPES_ALLOWED */ /* Do not insert code here. */ { op = (an_expr_operator_kind)eok_land; } /* if */ break; case tok_or_or: #if GNU_VECTOR_TYPES_ALLOWED if (type_kind == (a_type_kind)tk_vector) { op = (an_expr_operator_kind)eok_vector_lor; } else #endif /* GNU_VECTOR_TYPES_ALLOWED */ /* Do not insert code here. */ { op = (an_expr_operator_kind)eok_lor; } /* if */ break; #if GNU_EXTENSIONS_ALLOWED case tok_gnu_min: op = (an_expr_operator_kind)eok_gnu_min; break; case tok_gnu_max: op = (an_expr_operator_kind)eok_gnu_max; break; #endif /* GNU_EXTENSIONS_ALLOWED */ case tok_assign: op = (an_expr_operator_kind)eok_assign; break; case tok_times_assign: op = (an_expr_operator_kind)eok_multiply_assign; break; case tok_divide_assign: op = (an_expr_operator_kind)eok_divide_assign; break; case tok_remainder_assign: op = (an_expr_operator_kind)eok_remainder_assign; break; case tok_plus_assign: op = (an_expr_operator_kind)(type_kind == (a_type_kind)tk_pointer ? eok_padd_assign : eok_add_assign); break; case tok_minus_assign: op = (an_expr_operator_kind)(type_kind == (a_type_kind)tk_pointer ? eok_psubtract_assign : eok_subtract_assign); break; case tok_shift_left_assign: op = (an_expr_operator_kind)eok_shiftl_assign; break; case tok_shift_right_assign: op = (an_expr_operator_kind)eok_shiftr_assign; break; case tok_and_assign: op = (an_expr_operator_kind)eok_and_assign; break; case tok_excl_or_assign: op = (an_expr_operator_kind)eok_xor_assign; break; case tok_or_assign: op = (an_expr_operator_kind)eok_or_assign; break; default: unexpected_condition_str("which_binary_operator: bad int operator"); } /* switch */ if (type_kind == (a_type_kind)tk_error) { op = (an_expr_operator_kind)eok_error; } /* if */ return op; } /* which_binary_operator */ an_expr_operator_kind operator_for_opname_kind(an_opname_kind kind, a_boolean unary_operator) /* Return the operator (eok_...) that corresponds to the indicated operator kind. The operation is a unary operation if unary_operator is TRUE. */ { an_expr_operator_kind op = (an_expr_operator_kind)eok_error; if (unary_operator) { /* Unary operations. */ switch (kind) { case onk_plus: op = (an_expr_operator_kind)eok_unary_plus; break; case onk_minus: op = (an_expr_operator_kind)eok_negate; break; case onk_star: op = (an_expr_operator_kind)eok_indirect; break; case onk_ampersand: op = (an_expr_operator_kind)eok_address_of; break; case onk_compl: op = (an_expr_operator_kind)eok_complement; break; case onk_not: op = (an_expr_operator_kind)eok_not; break; case onk_plus_plus: /* Note that postfix ++ comes in as a non-unary operation. */ op = (an_expr_operator_kind)eok_pre_incr; break; case onk_minus_minus: /* Note that postfix -- comes in as a non-unary operation. */ op = (an_expr_operator_kind)eok_pre_decr; break; case onk_await: /* Only used in template-dependent contexts. */ op = (an_expr_operator_kind)eok_await; break; case onk_arrow: /* Only used in template-dependent contexts. */ op = (an_expr_operator_kind)eok_points_to_field; break; default: unexpected_condition_str("bad unary opname kind"); } /* switch */ } else { /* Non-unary operations. */ switch (kind) { case onk_plus: op = (an_expr_operator_kind)eok_add; break; case onk_minus: op = (an_expr_operator_kind)eok_subtract; break; case onk_star: op = (an_expr_operator_kind)eok_multiply; break; case onk_divide: op = (an_expr_operator_kind)eok_divide; break; case onk_remainder: op = (an_expr_operator_kind)eok_remainder; break; case onk_excl_or: op = (an_expr_operator_kind)eok_xor; break; case onk_ampersand: op = (an_expr_operator_kind)eok_and; break; case onk_or: op = (an_expr_operator_kind)eok_or; break; #if GNU_EXTENSIONS_ALLOWED case onk_gnu_min: op = (an_expr_operator_kind)eok_gnu_min; break; case onk_gnu_max: op = (an_expr_operator_kind)eok_gnu_max; break; #endif /* GNU_EXTENSIONS_ALLOWED */ case onk_assign: op = (an_expr_operator_kind)eok_assign; break; case onk_lt: op = (an_expr_operator_kind)eok_lt; break; case onk_gt: op = (an_expr_operator_kind)eok_gt; break; case onk_plus_assign: op = (an_expr_operator_kind)eok_add_assign; break; case onk_minus_assign: op = (an_expr_operator_kind)eok_subtract_assign; break; case onk_times_assign: op = (an_expr_operator_kind)eok_multiply_assign; break; case onk_divide_assign: op = (an_expr_operator_kind)eok_divide_assign; break; case onk_remainder_assign: op = (an_expr_operator_kind)eok_remainder_assign; break; case onk_excl_or_assign: op = (an_expr_operator_kind)eok_xor_assign; break; case onk_and_assign: op = (an_expr_operator_kind)eok_and_assign; break; case onk_or_assign: op = (an_expr_operator_kind)eok_or_assign; break; case onk_shift_left: op = (an_expr_operator_kind)eok_shiftl; break; case onk_shift_right: op = (an_expr_operator_kind)eok_shiftr; break; case onk_shift_right_assign: op = (an_expr_operator_kind)eok_shiftr_assign; break; case onk_shift_left_assign: op = (an_expr_operator_kind)eok_shiftl_assign; break; case onk_eq: op = (an_expr_operator_kind)eok_eq; break; case onk_ne: op = (an_expr_operator_kind)eok_ne; break; case onk_le: op = (an_expr_operator_kind)eok_le; break; case onk_ge: op = (an_expr_operator_kind)eok_ge; break; case onk_spaceship: op = (an_expr_operator_kind)eok_spaceship; break; case onk_and_and: op = (an_expr_operator_kind)eok_land; break; case onk_or_or: op = (an_expr_operator_kind)eok_lor; break; case onk_plus_plus: /* Note that postfix ++ comes in as a non-unary operation. */ op = (an_expr_operator_kind)eok_post_incr; break; case onk_minus_minus: /* Note that postfix -- comes in as a non-unary operation. */ op = (an_expr_operator_kind)eok_post_decr; break; case onk_comma: op = (an_expr_operator_kind)eok_comma; break; case onk_arrow_star: op = (an_expr_operator_kind)eok_pm_points_to_field; break; case onk_subscript: op = (an_expr_operator_kind)eok_subscript; break; default: unexpected_condition_str("bad opname kind"); } /* switch */ } /* if */ return op; } /* operator_for_opname_kind */ #if MICROSOFT_EXTENSIONS_ALLOWED an_opname_kind simple_opname_kind_for_compound_assignment(an_opname_kind kind) /* If kind is some kind of compound assignment, return the opname kind for the corresponding simple operator, e.g., onk_plus_assign ("+=") in produces onk_plus ("+") out. If kind is not a compound assignment operator, return onk_none. */ { an_opname_kind simple_kind = (an_opname_kind)onk_none; switch (kind) { case onk_plus_assign: simple_kind = (an_opname_kind)onk_plus; break; case onk_minus_assign: simple_kind = (an_opname_kind)onk_minus; break; case onk_times_assign: simple_kind = (an_opname_kind)onk_star; break; case onk_divide_assign: simple_kind = (an_opname_kind)onk_divide; break; case onk_remainder_assign: simple_kind = (an_opname_kind)onk_remainder; break; case onk_excl_or_assign: simple_kind = (an_opname_kind)onk_excl_or; break; case onk_and_assign: simple_kind = (an_opname_kind)onk_ampersand; break; case onk_or_assign: simple_kind = (an_opname_kind)onk_or; break; case onk_shift_left_assign: simple_kind = (an_opname_kind)onk_shift_left; break; case onk_shift_right_assign: simple_kind = (an_opname_kind)onk_shift_right; break; default: break; } /* switch */ return simple_kind; } /* simple_opname_kind_for_compound_assignment */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ void do_binary_operation_full(an_expr_operator_kind op, an_operand *operand_1, an_operand *operand_2, a_type_ptr result_type, a_boolean result_is_lvalue, an_operand *result, a_source_position *operator_position, a_token_sequence_number operator_tok_seq_number, a_source_position *operator_position_2) /* Perform a binary operation on 2 operands yielding a result. op indicates the operation, and operand_1 and operand_2 are the operands. result_type indicates the type of result. The result is an lvalue if result_is_lvalue is TRUE. The result is placed in *result. If the operands are constant, the operation will be folded if possible. operator_position indicates the operator position. operator_tok_seq_number indicates the token sequence number of the operator. operator_position_2, if non-NULL, indicates the position of a second operator (e.g., the "]" of a subscript operation). */ { a_boolean did_not_fold, template_constant; a_boolean just_past_end; a_boolean try_folding; #if GNU_EXTENSIONS_ALLOWED a_constant_ptr con_1 = local_constant(), con_2 = local_constant(); a_variable_ptr vp1, vp2; #endif /* GNU_EXTENSIONS_ALLOWED */ if (is_error_operand(operand_1) || is_error_operand(operand_2)) { make_error_operand(result); } else { /* Some addressing operations should not be folded in some nonconstant contexts, because the expression form provides more explicit addressing information (which is useful for aliasing analysis). */ /* Field-selection operations don't come through this routine, so there's no point in checking them. */ if (result_is_lvalue) { /* Can't fold to a constant for an lvalue result. */ try_folding = FALSE; } else if (!expr_stack->potentially_evaluated && !expr_stack->favor_constant_result && !(C_mode() && expr_stack->inside_conditional_expression)) { /* No need to fold in an unevaluated expression. There are some exceptions: - the operand of __builtin_constant_p (which also sets the expr_stack->favor_constant_result flag), and - C-mode conditional expressions because null pointer constants have to be recognized (e.g., "1 ? (void*)(2-2) : (int*)0" has type "int*" because (void*)(2-2) is treated as a null pointer constant). */ try_folding = FALSE; } else if (expr_stack->consteval_call_need_not_fold && !expr_stack->template_deduction_context && !(clang_version_is(<190000) && expr_stack->is_enumerator_value)) { /* This is a context that will require a constant expression at the top level, so there is no need to fold intermediate expressions. Folding intermediate expressions anyway could reduce the quality of diagnostics in some configurations (e.g., if a static_assert comparison fails, the early folding may prevent a report of the compared values). Prior to version 19, Clang appears not to diagnose integer overflow errors in expressions for enumerator constants. E.g.: "enum E { e = INT_MAX+1 };" is accepted. We approximate this by performing early folding (in folding.c instead of interpreter.c) and lowering the severity in issue_folding_diagnostic. */ try_folding = FALSE; } else if (op == eok_psubtract || op == eok_padd) { /* Try folding only if that's desirable in the current expression. */ try_folding = expr_stack->favor_constant_result; } else { /* Not an addressing operation (normal case). */ try_folding = TRUE; } /* if */ /* Try to fold the operation if both operands are constants and the current expression is being evaluated. */ did_not_fold = TRUE; template_constant = FALSE; if (try_folding) { force_operand_to_constant_if_possible(operand_1); force_operand_to_constant_if_possible(operand_2); } /* if */ if (!try_folding) { /* Do not try to fold the operator. */ } else if (is_constant_operand(operand_1) && is_constant_operand(operand_2)) { /* If the operator could not be determined (because the operand types are incompatible), fold the operation to an error operand. */ if (op == (an_expr_operator_kind)eok_error) { make_error_operand(result); did_not_fold = FALSE; } else { clear_operand(ok_constant, result); result->type = result_type; result->state = os_prvalue; /* Fold the operation on constants to produce a constant result. */ /* In a nonconstant context, reduce any error to a warning and leave the operation to be done at runtime. */ expr_binary_operation(op, &operand_1->variant.constant, &operand_2->variant.constant, result_type, &result->variant.constant, &did_not_fold, &template_constant, operator_position); if (!did_not_fold) { result->variant.constant.folded_statement_expression = operand_1->variant.constant.folded_statement_expression || operand_2->variant.constant.folded_statement_expression; } /* if */ } /* if */ } else if (op == eok_lor && constexpr_enabled && !expr_stack->possible_rescan_context && is_constant_operand(operand_1) && constant_bool_value_known_at_compile_time( &operand_1->variant.constant) && !op_is_false_constant(operand_1)) { /* (1 || nonconstant) produces true in C++11. */ eliminate_stmt_expr_refs_if_operand_dropped(operand_2); discard_operand(operand_2); make_integer_constant_operand(result, (a_host_large_integer)1); cast_operand(result_type, result, /*is_implicit_cast=*/TRUE); did_not_fold = FALSE; } else if (op == eok_land && constexpr_enabled && !expr_stack->possible_rescan_context && is_constant_operand(operand_1) && constant_bool_value_known_at_compile_time( &operand_1->variant.constant) && op_is_false_constant(operand_1)) { /* (0 && nonconstant) produces false in C++11. */ eliminate_stmt_expr_refs_if_operand_dropped(operand_2); discard_operand(operand_2); make_integer_constant_operand(result, (a_host_large_integer)0); cast_operand(result_type, result, /*is_implicit_cast=*/TRUE); did_not_fold = FALSE; #if GNU_EXTENSIONS_ALLOWED } else if ((op == eok_pdiff || op == eok_eq || op == eok_ne) && gcc_mode && is_expression_operand(operand_1) && is_expression_operand(operand_2) && is_pointer_type(operand_1->type) && is_pointer_type(operand_2->type) && constant_prvalue_pointer_full( operand_1->variant.expression, con_1, /*address_escapes=*/FALSE, CAO_TREAT_LOCAL_VAR_ADDR_AS_CONSTANT, (a_boolean *)NULL) && constant_prvalue_pointer_full( operand_2->variant.expression, con_2, /*address_escapes=*/FALSE, CAO_TREAT_LOCAL_VAR_ADDR_AS_CONSTANT, (a_boolean *)NULL)) { /* gcc allows a pointer difference or equality comparison of two pointer values based on addresses of the same local variable to be folded to a constant. */ clear_operand(ok_constant, result); result->type = result_type; result->state = os_prvalue; expr_binary_operation(op, con_1, con_2, result_type, &result->variant.constant, &did_not_fold, &template_constant, operator_position); if (!did_not_fold) { result->variant.constant.folded_statement_expression = con_1->folded_statement_expression || con_2->folded_statement_expression; } /* if */ } else if ((op == eok_eq || op == eok_ne) && (gcc_version_is(any_version) || (gpp_version_is(<60000) && constexpr_enabled)) && operand_is_prvalue_for_variable(operand_1, &vp1) && operand_is_prvalue_for_variable(operand_2, &vp2) && vp1 == vp2 && !is_floating_type(/*lint !e413*/vp1->type) && !is_volatile_qualified_type(/*lint !e413*/vp1->type)) { /* Similarly GCC folds x==x and x!=x, for identical variables in C mode, except for floating-point type variables (since NaN != NaN) and volatile variables (since different reads can produce different values). GCC prior to version 6.x also does this in C++11 and later modes. */ clear_operand(ok_constant, result); result->type = result_type; result->state = os_prvalue; if (op != eok_eq) { make_zero_of_proper_type(result_type, &result->variant.constant); } else { make_one_of_proper_type(result_type, &result->variant.constant); } /* if */ did_not_fold = FALSE; #endif /* GNU_EXTENSIONS_ALLOWED */ } /* if */ if (did_not_fold) { if (constexpr_enabled && (curr_expr_kind_is(ek_integral_constant) || curr_expr_kind_is(ek_template_arg) || (curr_expr_kind_is(ek_init_constant) && scope_stack_top().decl_parse_state != NULL && scope_stack_top().decl_parse_state->sym != NULL && (symbol_is(scope_stack_top().decl_parse_state->sym, sk_variable) || symbol_is(scope_stack_top().decl_parse_state->sym, sk_static_data_member)) && variable_for_symbol(scope_stack_top().decl_parse_state->sym) ->is_prototype_instantiation)) && (is_template_param_constant_operand(operand_1) || is_template_param_constant_operand(operand_2) || is_template_dependent_type(operand_1->type) || is_template_dependent_type(operand_2->type))) { /* This is a dependent expression used in a constant context, so make a template constant result instead of reporting it as being non-constant. */ template_constant = TRUE; } /* if */ if (!template_constant && curr_expr_kind_is_evaluated_const() && !constexpr_enabled && !result_is_lvalue && !(expr_stack != NULL && curr_expr_is_potentially_unevaluated())) { /* An operation on constants could not be folded. For example, a pointer comparison between pointers that aren't in the same object can't be represented as a constant. In a constant expression, that's an error. Cases that produce lvalues (like eok_subscript) are let by; that allows folding of "abc"[1] later, and there will be an lvalue-to-rvalue conversion that will flag an error if the address of the entity is not taken. In addition, potentially-unevaluated expressions are permitted not to fold; if the expression turns out eventually to be evaluated, the unfolded expression will result in an error at that point. */ expr_pos_error(ec_expr_not_constant, operator_position); make_error_operand(result); } else { /* The constant operation was not folded; create an expression operand. */ build_binary_result_operand_full(operand_1, operand_2, op, result_type, result_is_lvalue, result); /* Check for invalid constant subscripts. Also check for invalid pointer addition and subtraction when the pointer operand is not constant. */ if (is_expression_operand(result)) { an_error_code err_code; if (!valid_node_if_subscript(result->variant.expression, &just_past_end, &err_code)) { expr_pos_warning(err_code, operator_position); } /* if */ } /* if */ if (template_constant) { /* For an expression based on a template parameter, scanned during the prototype instantiation, make a ck_template_param constant for the result. */ make_template_param_expr_constant_operand(result); } /* if */ } /* if */ } else if (curr_expr_kind_is_one_in_which_const_exprs_are_recorded()) { /* The operation was folded to a constant. We're recording expressions for constants, so build an expression and point the constant to it. */ an_operand result_expr; check_assertion(!result_is_lvalue); build_binary_result_operand(operand_1, operand_2, op, result_type, &result_expr); if (!is_error_operand(&result_expr)) { check_assertion(is_expression_operand(&result_expr)); result->variant.constant.expr = result_expr.variant.expression; } /* if */ } else { if (is_constant_operand(operand_1)) { eliminate_stmt_expr_refs_if_constant_dropped( &operand_1->variant.constant); } /* if */ if (is_constant_operand(operand_2)) { eliminate_stmt_expr_refs_if_constant_dropped( &operand_2->variant.constant); } /* if */ } /* if */ } /* if */ result->ruled_out_expr_kinds = (operand_1->ruled_out_expr_kinds | operand_2->ruled_out_expr_kinds); record_operator_position_in_rescan_info(result, operator_position, operator_tok_seq_number, operator_position_2); #if GNU_EXTENSIONS_ALLOWED release_local_constant(&con_1); release_local_constant(&con_2); #endif /* GNU_EXTENSIONS_ALLOWED */ } /* do_binary_operation_full */ void do_binary_operation(an_expr_operator_kind op, an_operand *operand_1, an_operand *operand_2, a_type_ptr result_type, an_operand *result, a_source_position *operator_position, a_token_sequence_number operator_tok_seq_number) /* Interface to do_binary_operation_full for the usual case where the result is a prvalue. See do_binary_operation_full for a description of the parameters. */ { do_binary_operation_full(op, operand_1, operand_2, result_type, /*result_is_lvalue=*/FALSE, result, operator_position, operator_tok_seq_number, (a_source_position *)NULL); } /* do_binary_operation */ static a_boolean expr_has_uncertain_value_category(an_expr_node_ptr expr) /* Sometimes a template-dependent expression can't be known to be an lvalue or an rvalue (e.g., T::x might be an lvalue if x is a static data member, or an rvalue if x is an enumerator). Return TRUE if the value category of the given expression is uncertain. The safe answer is TRUE. An error node is considered to have uncertain value category even outside of a template-dependent context. */ { a_boolean uncertain = FALSE; if (is_error_node(expr) || is_error_type(expr->type)) { uncertain = TRUE; } else if (is_template_dependent_type(expr->type)) { uncertain = TRUE; /* In some cases, we can tell that something is really an lvalue or rvalue based on the operator. */ if (is_operation_node(expr)) { an_expr_node_ptr op1 = expr->variant.operation.operands; switch (expr->variant.operation.kind) { case eok_ref_indirect: /* A reference indirection always produces an lvalue. */ uncertain = FALSE; break; case eok_indirect: if (is_pointer_type(op1->type)) { /* A "*" returns an lvalue unless it's overloaded. */ uncertain = FALSE; } /* if */ break; case eok_lvalue: /* An eok_lvalue produces an lvalue by definition. */ uncertain = FALSE; break; case eok_cast: if (!could_be_dependent_class_type(expr->type) && !could_be_dependent_class_type(op1->type) && !is_error_type(op1->type)) { /* Casts other than those involving unknown dependent classes have known value categories. */ uncertain = FALSE; } /* if */ break; default: break; } /* switch */ } /* if */ } /* if */ return uncertain; } /* expr_has_uncertain_value_category */ a_boolean operand_has_uncertain_value_category(an_operand *operand) /* Sometimes a template-dependent operand can't be known to be an lvalue or an rvalue (e.g., T::x might be an lvalue if x is a static data member, or an rvalue if x is an enumerator). Return TRUE if the value category of the given operand is uncertain. The safe answer is TRUE. An error operand is considered to have uncertain value category even outside of a template-dependent context. */ { a_boolean uncertain = FALSE; if (is_error_operand(operand)) { uncertain = TRUE; } else if (!is_template_dependent_context()) { uncertain = FALSE; } else if (is_expression_operand(operand)) { an_expr_node_ptr expr = operand->variant.expression; uncertain = expr_has_uncertain_value_category(expr); } else if (is_template_dependent_type(operand->type)) { uncertain = TRUE; if (is_constant_operand(operand)) { a_constant_ptr con = &operand->variant.constant; if (con->kind == (a_constant_repr_kind)ck_template_param && con->variant.template_param.kind == (a_template_param_constant_kind)tpck_expression) { /* A tpck_expression constant is uncertain if the expression is uncertain. */ uncertain = expr_has_uncertain_value_category( expr_node_from_tpck_expression(con)); } /* if */ } /* if */ } /* if */ return uncertain; } /* operand_has_uncertain_value_category */ static void do_generic_operand_transformations(an_operand *operand, a_boolean force_to_rvalue) /* Do transformations that are appropriate on a generic operand, i.e., an operand in a template-dependent operation, where we can't tell what will be done with the operand. In particular, we can't tell whether it will be used as an rvalue or an lvalue, unless force_to_rvalue is TRUE, in which case we force the operand to be an rvalue. */ { check_assertion(is_template_dependent_context()); if (is_indefinite_function_operand(operand)) { /* Replace an indefinite function by the address of an unknown function in the set. The result is always an rvalue. */ conv_indefinite_function_to_unknown_dependent_function(operand, force_to_rvalue); force_to_rvalue = FALSE; } else if (is_sym_for_member_operand(operand)) { /* Replace a symbol-for-member operand by a pointer-to-member. */ conv_sym_for_member_operand_to_ptr_to_member(operand, (a_source_position *)NULL); } /* if */ if (force_to_rvalue) { do_operand_transformations(operand, TOPT_NO_OPTIONS); } /* if */ } /* do_generic_operand_transformations */ static void do_rvalue_generic_operand_transformations(an_operand *operand) /* Do transformations that are appropriate for a generic operand, i.e., an operand in a template-dependent operation, when it is known that the operand will definitely be used as an rvalue. */ { do_generic_operand_transformations(operand, /*force_to_rvalue=*/TRUE); } /* do_rvalue_generic_operand_transformations */ static void do_constant_generic_operand_transformations(an_operand *operand) /* Do transformations that are appropriate on a generic operand, i.e., an operand in a template-dependent operation, in a context that is part of a constant expression and in which the operand will definitely be used as an rvalue. */ { check_assertion(curr_expr_kind_is_const()); do_rvalue_generic_operand_transformations(operand); check_assertion(is_constant_operand(operand) || constexpr_enabled || is_error_operand(operand) || !curr_expr_kind_is_traditional_const()); } /* do_constant_generic_operand_transformations */ void prep_generic_operand_full(an_operand *operand, a_boolean lvalue_expected, a_boolean rvalue_expected) /* The indicated operand is about to be used as the operand of an expression involving template parameter types. lvalue_expected is TRUE to indicate that an lvalue is expected/required, and rvalue_expected is TRUE to indicate that an rvalue is expected/required. If neither is TRUE, any value category is acceptable in the context where the expression will be used. An eok_lvalue node will be inserted if necessary to adjust the value category of the expression. If the operand is a braced-init-list, its elements are prepared as well so that local variables used only there are marked referenced. */ { an_expr_node_ptr expr; an_operand orig_operand; check_assertion(is_template_dependent_context()); orig_operand = *operand; if (is_braced_init_list_operand(operand)) { /* References for a braced-init-list live on the element operands, not on the list operand itself. */ prep_generic_init_component_list( operand->variant.braced_init_list->variant.braced.list); } else if (is_indefinite_function_operand(operand)) { a_symbol_ptr ovl_sym = operand->symbol; if (!ovl_sym->is_class_member) { /* Record that this particular name was referenced in a dependent context so we can suppress warnings on static functions that appear unreferenced but might be chosen by a dependent call. */ ovl_sym->header->any_function_referenced_in_dependent_call = TRUE; } /* if */ } /* if */ if (rvalue_expected) { if (curr_expr_kind_is_const()) { do_constant_generic_operand_transformations(operand); } else { do_rvalue_generic_operand_transformations(operand); } /* if */ } else { if (lvalue_expected) { change_nonreal_member_constant_operand_to_lvalue(operand); } /* if */ do_generic_operand_transformations(operand, /*force_to_rvalue=*/FALSE); } /* if */ if (is_an_lvalue(operand) || is_a_function_designator(operand)) { check_assertion(!rvalue_expected); } else if (is_an_rvalue(operand) || is_braced_init_list_operand(operand)) { if (lvalue_expected) { /* The operand is an rvalue, and the operation expects an lvalue. Add an eok_lvalue node. */ expr = make_node_from_operand(operand); expr = make_lvalue_operator_node((an_expr_operator_kind)eok_lvalue, expr->type, expr); make_glvalue_expression_operand(expr, operand); } /* if */ } /* if */ restore_operand_details_incl_ref(operand, &orig_operand); /* We don't know how this operand is used, so set a special kind of reference. */ change_ref_kinds(operand->ref_entries_list, SRK_PROTO_INST_REF); } /* prep_generic_operand_full */ void prep_generic_operand(an_operand *operand) /* The indicated operand is about to be used as the operand of an expression involving template parameter types. Adjust it as needed. We don't know whether the operand will be used as an lvalue or an rvalue. */ { prep_generic_operand_full(operand, /*lvalue_expected=*/FALSE, /*rvalue_expected=*/FALSE); } /* prep_generic_operand */ static void prep_generic_init_component_list(an_arg_list_elem_ptr list) /* Prepare the elements of an initializer list that appears as an operand of a template-dependent operation. Nested brace-enclosed lists are walked recursively. */ { an_arg_list_elem_ptr alep; for (alep = list; alep != NULL; alep = next_elem(alep)) { if (is_expression_component(alep)) { prep_generic_operand(operand_of_arg_list_elem(alep)); } else if (is_braced_init_component(alep)) { prep_generic_init_component_list(alep->variant.braced.list); } /* if */ } /* for */ } /* prep_generic_init_component_list */ void generic_cast_operand(an_operand *operand, a_type_ptr dest_type, a_cast_source_form source_form, a_boolean is_implicit_cast) /* Add a generic cast that casts the given operand to dest_type. This is used in prototype instantiations to represent conversions to unknown types. source_form identifies the kind of cast (e.g., static_cast, const_cast). is_implicit_cast is TRUE if the cast is implicit. dest_type is allowed to be a reference type. Note that the cast can be bizarre in a number of ways, e.g., if the source operand is an lvalue. */ { a_type_ptr orig_dest_type = dest_type; an_operand orig_operand; a_boolean rvalue_expected = FALSE, lvalue_expected = FALSE; a_boolean is_reference_cast = is_any_reference_type(dest_type); a_boolean is_rvalue_reference_cast = FALSE; orig_operand = *operand; check_assertion(is_template_dependent_context()); /* See whether we know that the operand will be used as an lvalue or rvalue. */ if (is_reference_cast) { a_type_ptr underlying_tp = type_pointed_to(dest_type); if (is_rvalue_reference_type(dest_type)) { if (is_template_param_type(underlying_tp)) { /* Something like T&& or A::X&& is not guaranteed to be an rvalue reference because of the reference-collapsing rules. */ } else { is_rvalue_reference_cast = TRUE; rvalue_expected = TRUE; } /* if */ } else { lvalue_expected = TRUE; } /* if */ /* Don't lose a type operator that appears on top of the reference type, but otherwise replace the reference type with the referenced type. */ if (!(type_is(dest_type, tk_typeref) && typeref_is_type_operator(dest_type))) { dest_type = underlying_tp; } /* if */ } else if (!curr_expr_kind_is_const() && (is_class_struct_union_type(dest_type) || is_template_param_type(dest_type) || is_class_struct_union_type(operand->type) || is_template_param_type(operand->type))) { /* This might be a cast to or from a class type, and thus might involve a user-defined conversion. Therefore we don't know whether the operand will be used as an lvalue or an rvalue. Or, the destination type is a type that might turn out to be a reference type, which means the operand might be used as an lvalue. */ } else { /* The operand will definitely be used as an rvalue. */ rvalue_expected = TRUE; } /* if */ prep_generic_operand_full(operand, lvalue_expected, rvalue_expected); if (is_error_operand(operand)) { normalize_error_operand(operand); } else if (is_undefined_symbol_operand(operand)) { /* The undefined symbol is about to be the operand of some operation other than a call, so it's truly undefined. */ if (expr_error_should_be_issued()) { str_error(ec_undefined_identifier, operand->symbol->header->identifier); } /* if */ make_error_operand(operand); } else { /* Generate a cast expression. For an implicit conversion, it's okay to add nothing at all. If template_constant is TRUE, we will later put the resulting expression under a tpck_expression constant. */ a_boolean template_constant = FALSE; if (!is_reference_cast && is_constant_operand(operand) && is_an_rvalue(operand) && !is_class_struct_union_type(dest_type)) { template_constant = TRUE; } /* if */ if (!is_implicit_cast || !cast_identical_types(operand->type, dest_type)) { an_expr_node_ptr expr, opexpr; if (is_rvalue_reference_cast) { /* The language says that the operand of an rvalue reference cast is an rvalue, which is ensured above. However, the IL operators take an lvalue as their operand, so adjust for that. */ prep_generic_operand_full(operand, /*lvalue_expected=*/TRUE, /*rvalue_expected=*/FALSE); } /* if */ if (source_form == csf_functional) { opexpr = make_node_from_operand_for_expr_list(operand); } else { opexpr = make_node_from_operand(operand); } /* if */ /* Certain kinds of casts are known not to create a new object. For others that might create a class object, use an enk_temp_init form. GCC allows casts to incomplete class types in unevaluated expressions that appear in dependent contexts, and if the types involved are nondependent the resulting construct is also treated as nondependent: Use a cast operation instead of an enk_temp_init node for those cases, since an enk_temp_init node would be treated as instantiation-dependent. */ if (source_form == csf_const_cast || source_form == csf_reinterpret_cast || source_form == csf_dynamic_cast || !is_class_struct_union_type(dest_type) || is_reference_cast || (gpp_mode && !clang_mode && !expr_stack->potentially_evaluated && is_incomplete_type(dest_type))) { /* Render the cast as a cast operator. */ an_expr_operator_kind op; if (source_form == csf_dynamic_cast) { if (is_reference_cast) { op = (an_expr_operator_kind)eok_ref_dynamic_cast; } else { op = (an_expr_operator_kind)eok_dynamic_cast; } /* if */ } else if (is_reference_cast) { op = (an_expr_operator_kind)eok_ref_cast; } else { op = (an_expr_operator_kind)eok_cast; } /* if */ if (!is_reference_cast && (opexpr->is_lvalue || opexpr->is_xvalue) && !is_instantiation_dependent_type(dest_type) && !is_instantiation_dependent_type(opexpr->type)) { /* Ensure consistency of value categories for the nondependent cases that get here. That is particularly needed when targeting the C++-generating back end because it checks that consistency even in prototype instantiations. */ conv_glvalue_expr_to_prvalue(opexpr, /*constant_case=*/NULL, /*con_value=*/NULL, /*err_pos=*/NULL); } /* if */ expr = make_operator_node(op, dest_type, opexpr); if (is_implicit_cast) { expr->position = opexpr->position; expr->compiler_generated = TRUE; } /* if */ if (is_reference_cast) { mark_as_reference_cast(expr, orig_dest_type); expr->is_lvalue = TRUE; /* The expression will be changed to an xvalue below. */ } /* if */ } else { /* Cast to a class type. Use an enk_temp_init/dik_constructor. */ a_dynamic_init_ptr dip; expr = create_expr_temporary(dest_type, /*is_lvalue=*/FALSE, /*is_explicit_cast=*/!is_implicit_cast, /*suppress_abstract_test=*/FALSE, (a_dynamic_init_kind)dik_constructor, &orig_operand.position, &dip); /* A dik_constructor with a NULL constructor is used for generic construction. */ dip->variant.constructor.ptr = NULL; dip->variant.constructor.args = opexpr; } /* if */ /* Remember the source form of the cast. */ switch (source_form) { case csf_none: case csf_old_style: case csf_functional: break; case csf_static_cast: expr->is_static_cast = TRUE; break; #if MICROSOFT_EXTENSIONS_ALLOWED case csf_safe_cast: expr->is_safe_cast = TRUE; break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case csf_const_cast: check_assertion(is_operation_node(expr)); expr->variant.operation.is_const_cast = TRUE; break; case csf_reinterpret_cast: check_assertion(is_operation_node(expr)); expr->variant.operation.is_reinterpret_cast = TRUE; break; case csf_dynamic_cast: /* This case is encoded in the operator above. */ break; default: unexpected_condition(); } /* switch */ make_lvalue_or_rvalue_expression_operand(expr, operand); if (is_rvalue_reference_cast) { conv_rvalue_reference_result_to_xvalue(operand); } /* if */ if (template_constant) { make_template_param_expr_constant_operand(operand); } /* if */ } /* if */ } /* if */ restore_operand_details_for_cast(operand, &orig_operand, is_implicit_cast, /*incl_ref=*/TRUE); operand->is_id_expression = FALSE; operand->id_expression_was_parenthesized = FALSE; } /* generic_cast_operand */ void make_generic_designator_constant(an_arg_list_elem_ptr icp, a_constant_ptr con) /* Transfer the generic designator description in icp to the ck_designator constant pointed to by con. */ { con->type = void_type(); con->variant.designator.is_generic = TRUE; if (icp->variant.designator.field_name != NULL) { con->variant.designator.is_field_designator = TRUE; con->variant.designator.variant.field_name = icp->variant.designator.field_name->identifier; } else { a_constant_ptr first = icp->variant.designator.element_index, last = icp->variant.designator.last_element_index; first->next = first != last ? last : (a_constant*)NULL; con->variant.designator.variant.subscript = first; if (scope_stack_top().in_template_deduction_context) { pos_error(ec_array_designator_for_deduced_context, init_component_pos(icp)); } /* if */ } /* if */ } /* make_generic_designator_constant */ void prep_generic_argument(an_arg_list_elem_ptr arg) /* Prepare a generic argument, i.e., an argument of a call scanned during a prototype instantiation, for which we do not know the way the argument is going to be used. The argument can be an expression or a brace-enclosed list. */ { if (is_expression_component(arg)) { prep_generic_operand(operand_of_arg_list_elem(arg)); } else if (is_braced_init_component(arg)) { prep_generic_argument_list(arg->variant.braced.list); } else if (is_designator_component(arg)) { /* Change arg to an expression component representing a ck_designator constant. */ a_constant_ptr des_con = local_constant(); clear_constant(des_con, ck_designator); make_generic_designator_constant(arg, des_con); des_con->type = type_of_unknown_templ_param_nontype; set_init_component_kind(arg, ick_expression); make_constant_operand(des_con, operand_of_arg_list_elem(arg)); release_local_constant(&des_con); } else { unexpected_condition(); } /* if */ } /* prep_generic_argument */ void prep_generic_argument_list(an_arg_list_elem_ptr arg_list) /* Prepare a generic argument list, i.e., one scanned during a prototype instantiation for which we do not know the actual function to be called. */ { an_arg_list_elem_ptr arg; for (arg = arg_list; arg != NULL; arg = next_elem(arg)) { prep_generic_argument(arg); } /* for */ } /* prep_generic_argument_list */ void mark_init_component_list_as_permanently_allocated( an_init_component *list_icp) /* Mark the entries on the indicated init-component list, and their subtrees, as permanently allocated. */ { an_init_component_ptr icp; for (icp = list_icp; icp != NULL; icp = next_elem(icp)) { mark_init_component_as_permanently_allocated(icp); } /* for */ } /* mark_init_component_list_as_permanently_allocated */ static void mark_init_component_as_permanently_allocated( an_init_component *icp) /* Mark the indicated init-component and its subtree as permanently allocated. We don't plan to free it, and any attempt to free it will be ignored. */ { if (!icp->permanently_allocated) { icp->permanently_allocated = TRUE; #if CHECKING && DEBUG num_init_components_permanently_allocated++; #endif /* CHECKING && DEBUG */ if (is_braced_init_component(icp)) { mark_init_component_list_as_permanently_allocated( icp->variant.braced.list); } /* if */ } /* if */ } /* mark_init_component_as_permanently_allocated */ void make_braced_init_list_operand(an_arg_list_elem_ptr alep, an_operand *result) /* Make an ok_braced_init_list operand from an init-component that is a brace- enclosed list. */ { check_assertion(is_braced_init_component(alep)); clear_operand((an_operand_kind)ok_braced_init_list, result); result->variant.braced_init_list = alep; result->type = unknown_type(); result->position = *init_component_pos(alep); #if EXTRA_SOURCE_POSITIONS_IN_IL result->end_position = *init_component_end_pos(alep); #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ mark_init_component_as_permanently_allocated(alep); } /* make_braced_init_list_operand */ static an_expr_node_ptr make_braced_init_expr_from_arg_list_elem( an_arg_list_elem_ptr alep) /* Make an enk_braced_init_list expression node from the given argument list entry (which is an ick_braced entry) and return a pointer to it. enk_braced_init_list nodes are used in template prototype instantiations; in other contexts initializer lists always resolve to some other kind of expression (e.g., a constructor call). */ { an_expr_node_ptr node; a_source_position *pos = init_component_pos(alep); #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position *end_pos = init_component_end_pos(alep); #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ check_assertion(is_braced_init_component(alep)); node = alloc_expr_node((an_expr_node_kind)enk_braced_init_list); node->variant.braced_init_list = make_expr_list_from_argument_list(alep->variant.braced.list, /*dependent_expression=*/TRUE); node->type = type_of_unknown_templ_param_nontype; if (alep->pack_expansion_descr != NULL) { node->is_pack_expansion = TRUE; } /* if */ set_expr_position(node, pos, end_pos, (a_source_position *)NULL); if (expr_stack != NULL && expr_stack->possible_rescan_context) { /* In a potential rescan context, make a dummy operand that has the source position information and record it as rescan information. */ an_operand dummy_operand; make_braced_init_list_operand(alep, &dummy_operand); dummy_operand.pack_expansion_descr = alep->pack_expansion_descr; save_operand_info_in_expr_rescan_info_entry(&dummy_operand, node); } /* if */ return node; } /* make_braced_init_expr_from_arg_list_elem */ an_expr_node_ptr make_expr_from_argument(an_arg_list_elem_ptr arg) /* Make and return an expression for an argument in arg-list-element form. */ { an_expr_node_ptr expr; if (is_expression_component(arg)) { /* The argument is an expression. */ eliminate_unusual_operand_kinds(operand_of_arg_list_elem(arg)); expr = make_node_from_operand_for_expr_list(operand_of_arg_list_elem(arg)); } else if (is_designator_component(arg)) { if (expr_error_should_be_issued()) { pos_error(ec_unexpected_designator, init_component_pos(arg)); } /* if */ expr = error_node(); } else { /* The argument is a brace-enclosed list. */ check_assertion(is_braced_init_component(arg)); expr = make_braced_init_expr_from_arg_list_elem(arg); } /* if */ if (expr->extra.rescan_info != NULL && arg->pack_expansion_descr != NULL) { /* If this expression may need rescanning in the future and it is part of a pack expansion, make sure the pack expansion descriptor is available in the operand saved for rescanning. */ expr->extra.rescan_info->saved_operand.pack_expansion_descr = arg->pack_expansion_descr; } /* if */ return expr; } /* make_expr_from_argument */ an_expr_node_ptr make_expr_list_from_argument_list( an_arg_list_elem_ptr arg_list, a_boolean dependent_expression) /* Make and return an expression list for an argument list. Some of the arguments may be brace-enclosed lists, in prototype instantiations. If dependent_expression is TRUE, this is being done in a template-dependent context and the result is template-dependent. */ { an_expr_node_ptr expr, prev_expr, expr_list; an_arg_list_elem_ptr arg; prev_expr = NULL; expr_list = NULL; for (arg = arg_list; arg != NULL; arg = next_elem(arg)) { /* Ignore designator components in dependent expressions as we cannot represent this as an expression and don't know whether it's valid for instantiations. This will be diagnosed during instantiation if it's invalid. */ if (dependent_expression && is_designator_component(arg)) continue; expr = make_expr_from_argument(arg); /* Add this expression to the end of the expression-form list being built up. */ if (prev_expr == NULL) { expr_list = expr; } else { prev_expr->next = expr; } /* if */ prev_expr = expr; } /* for */ return expr_list; } /* make_expr_list_from_argument_list */ void make_dummy_lvalue_operand(a_type_ptr type, an_operand *operand) /* Make a placeholder lvalue operand whose type is "type". */ { an_expr_node_ptr expr; expr = make_dummy_lvalue_expr(type); make_glvalue_expression_operand(expr, operand); operand->is_dummy_lvalue = TRUE; } /* make_dummy_lvalue_operand */ void template_binary_operation(an_expr_operator_kind op, an_operand *operand_1, an_operand *operand_2, an_operand *result, a_source_position *operator_position, a_token_sequence_number operator_tok_seq_number, a_source_position *operator_position_2) /* This routine is a wrapper for do_binary_operation for the case where an expression is being built from operands whose types are based on template parameter types. When the current expression kind is constant, this can happen in nontype template arguments. Otherwise, it happens in prototype instantiations. op is the operator to be used. operator_position gives the source position of the operator, and operator_tok_seq_number gives its token sequence number. If operator_position_2 is non-NULL, it gives the source position of a secondary operator (e.g., the "]" of a subscript operation). */ { a_type_ptr result_type = type_of_unknown_templ_param_nontype; a_boolean known_not_overloaded = FALSE; if (curr_expr_kind_is_const()) { do_constant_generic_operand_transformations(operand_1); do_constant_generic_operand_transformations(operand_2); known_not_overloaded = TRUE; } else { /* The current expression is not a constant expression. */ if (!is_overloadable_type_operand_full(operand_1, /*first_operand=*/TRUE, /*CFOO_guard=*/FALSE) && !is_overloadable_type_operand_full(operand_2, /*first_operand=*/FALSE, /*CFOO_guard=*/FALSE)) { known_not_overloaded = TRUE; } /* if */ if (known_not_overloaded) { a_boolean op1_lvalue_expected = operator_takes_lvalue_op1(op); a_boolean op2_lvalue_expected = operator_takes_lvalue_op2(op); prep_generic_operand_full(operand_1, op1_lvalue_expected, !op1_lvalue_expected); prep_generic_operand_full(operand_2, op2_lvalue_expected, !op2_lvalue_expected); } else { prep_generic_operand(operand_1); prep_generic_operand(operand_2); } /* if */ } /* if */ if (known_not_overloaded) { /* In some cases, we know the result type even if we do not know the operand types, but only if we know that the operator cannot be overloaded. */ if (is_operator_returning_bool(op)) { result_type = boolean_result_type(); } /* if */ } /* if */ do_binary_operation_full(op, operand_1, operand_2, result_type, /*result_is_lvalue=*/FALSE, result, operator_position, operator_tok_seq_number, operator_position_2); } /* template_binary_operation */ void template_multi_subscript_operation( an_expr_operator_kind op_kind, an_operand *operand_1, an_arg_list_elem_ptr subscripts, an_operand *result, a_source_position *operator_position, a_token_sequence_number operator_tok_seq_number, a_source_position *operator_position_2) /* Similar to template_binary_operation, but used for a template-dependent C++/CLI or C++23 subscript operation, where the second operand is a list of subscript expression. */ { an_expr_node_ptr op_1_expr, subsc_exprs, op_expr; prep_generic_operand(operand_1); op_1_expr = make_node_from_operand(operand_1); prep_generic_argument_list(subscripts); subsc_exprs = make_expr_list_from_argument_list( subscripts, /*dependent_expression=*/TRUE); op_1_expr->next = subsc_exprs; op_expr = make_operator_node( op_kind, type_of_unknown_templ_param_nontype, op_1_expr); op_expr->variant.operation.eval_left_to_right = TRUE; make_expression_operand(op_expr, result); rule_out_expr_kinds(ROEK_CONSTANT, result); record_operator_position_in_rescan_info(result, operator_position, operator_tok_seq_number, operator_position_2); } /* template_multi_subscript_operation */ void do_unary_operation(an_expr_operator_kind op, an_operand *operand, a_type_ptr result_type, an_operand *result, a_source_position *start_position, a_token_sequence_number operator_tok_seq_number) /* Perform a unary operation on one operand yielding a result. op indicates the operation, and operand is the operand. result_type indicates the type of result; the result is placed in *result. If the operand is constant, the operation will be folded if possible. start_position indicates the operator position. operator_tok_seq_number indicates the token sequence number of the operator. */ { a_boolean did_not_fold, template_constant, try_folding; a_constant_ptr result_constant = local_constant(); if (is_error_operand(operand)) { make_error_operand(result); } else { did_not_fold = TRUE; template_constant = (operand_is_dependent(operand) || is_template_dependent_type(result_type)); if (op == (an_expr_operator_kind)eok_address_of || template_constant) { /* "&" doesn't get folded here. It isn't handled by unary_operation and the constant case would involve a constant-addressed lvalue, not a constant, but in addition this routine isn't called for the non-dependent source "&" operand, so the only cases that get here are template dependent cases. */ try_folding = FALSE; } else { try_folding = TRUE; } /* if */ if (try_folding) { force_operand_to_constant_if_possible(operand); if (is_constant_operand(operand)) { /* Fold the operation if the operand is constant. In a nonconstant context, reduce any error to a warning and leave the operation to be done at runtime. */ expr_unary_operation(op, &operand->variant.constant, result_type, result_constant, &did_not_fold, &template_constant, start_position); if (!did_not_fold) { result_constant->folded_statement_expression = operand->variant.constant.folded_statement_expression; } /* if */ } /* if */ } /* if */ if (did_not_fold) { if (!template_constant && curr_expr_kind_is_evaluated_const() && !constexpr_enabled) { /* A constant operation could not be folded in a constant expression. */ expr_pos_error(ec_expr_not_constant, start_position); make_error_operand(result); } else { /* The operation could not be folded to a constant, so build an expression node. */ build_unary_result_operand(operand, op, result_type, result); if (template_constant) { /* For an expression based on a template parameter, scanned during the prototype instantiation, make a ck_template_param constant for the result. */ make_template_param_expr_constant_operand(result); } /* if */ } /* if */ } else { /* The operation was folded to a constant. */ if (curr_expr_kind_is_one_in_which_const_exprs_are_recorded()) { /* The operation was folded to a constant. We're recording expressions for constants, so build an expression and point the constant to it. */ an_operand result_expr; build_unary_result_operand(operand, op, result_type, &result_expr); result_constant->expr = result_expr.variant.expression; } else if (is_constant_operand(operand)) { eliminate_stmt_expr_refs_if_constant_dropped( &operand->variant.constant); } /* if */ make_constant_operand(result_constant, result); } /* if */ } /* if */ result->ruled_out_expr_kinds = operand->ruled_out_expr_kinds; check_assertion(!result->is_operand_of_address_of); record_operator_position_in_rescan_info(result, start_position, operator_tok_seq_number, (a_source_position *)NULL); release_local_constant(&result_constant); } /* do_unary_operation */ void template_unary_operation(an_expr_operator_kind op, an_operand *operand, an_operand *result, a_source_position *start_position, a_token_sequence_number operator_tok_seq_number) /* This routine is a wrapper for do_unary_operation for the case where an expression is being built from an operand whose type is based on template parameter types. When the current expression kind is constant, this can happen in nontype template arguments. Otherwise, it happens in prototype instantiations. op is the operator to be used. */ { a_type_ptr result_type = type_of_unknown_templ_param_nontype; a_boolean known_not_overloaded = FALSE; a_boolean address_of_case = (op == (an_expr_operator_kind)eok_address_of); if (address_of_case) { /* For unary "&", do some special processing. */ /* If the operand is a member of a nonreal class, e.g., &T::x, make an lvalue for the member instead of the previous assumption that it is a constant. */ change_nonreal_member_constant_operand_to_lvalue(operand); if (is_a_prvalue(operand) && is_template_param_constant_operand(operand)) { change_template_param_constant_operand_to_lvalue(operand); } /* if */ } /* if */ if (curr_expr_kind_is_const()) { if (address_of_case) { /* For address-of in a constant expression, the operand is an lvalue. */ if (!is_an_lvalue(operand) && !is_a_function_designator(operand) && !is_error_operand(operand)) { error_in_operand(ec_expr_not_an_lvalue_or_function_designator, operand); } else { do_generic_operand_transformations(operand, /*force_to_rvalue=*/FALSE); } /* if */ } else { /* In all other cases, the operand is an rvalue. */ do_constant_generic_operand_transformations(operand); } /* if */ known_not_overloaded = TRUE; } else { /* The current expression is not a constant expression. */ if (!is_overloadable_type_operand_full(operand, /*first_operand=*/TRUE, /*CFOO_guard=*/FALSE)) { known_not_overloaded = TRUE; } /* if */ if (known_not_overloaded) { a_boolean lvalue_expected = operator_takes_lvalue_op1(op); prep_generic_operand_full(operand, lvalue_expected, !lvalue_expected); } else { prep_generic_operand(operand); } /* if */ } /* if */ if (known_not_overloaded) { /* In some cases, we know the result type even if we do not know the operand type, but only if we know that the operator cannot be overloaded. */ if (is_operator_returning_bool(op)) { result_type = boolean_result_type(); } /* if */ } /* if */ if (address_of_case && curr_expr_kind_is_const()) { /* "&" operator in a constant expression, which cannot be folded the usual way. */ if (is_an_lvalue(operand)) { copy_operand(operand, result); take_address_of_lvalue(result, start_position); } else if (is_a_function_designator(operand)) { copy_operand(operand, result); conv_function_designator_to_ptr_to_function(result, start_position, /*allow_ctor=*/FALSE, /*will_call=*/FALSE); } else { check_assertion(is_error_operand(operand)); make_error_operand(result); } /* if */ if (result->is_operand_of_address_of) { /* The "&" operator is implicit and its position is saved in the operand, so we don't try to save it again. */ } else { record_operator_position_in_rescan_info(result, start_position, operator_tok_seq_number, (a_source_position *)NULL); } /* if */ } else { /* Normal case. */ do_unary_operation(op, operand, result_type, result, start_position, operator_tok_seq_number); } /* if */ result->ruled_out_expr_kinds = operand->ruled_out_expr_kinds; } /* template_unary_operation */ static void temp_init_from_comma_operand(an_operand *operand) /* If operand represents a comma operation and the second operand is a temp-init node, convert the operand into a temp init from that node. */ { if (is_expression_operand(operand)) { an_expr_node_ptr node = skip_parens(operand->variant.expression); a_type_ptr unqual_type = skip_typerefs(operand->type); if (is_operation_node(node) && node_operator_is(node, eok_comma) && is_temp_node(node->variant.operation.operands->next)) { if ((mandatory_copy_elision || gnu_mode) && is_class_struct_union_type(unqual_type)) { /* In modes where we need to elide copies, check to see if elision applies. */ a_dynamic_init_ptr dip; a_conv_context_set conv_context = CCO_MOVE_OPTIMIZATION_ALLOWED; if (gnu_version_is(any_version)) { /* GCC treats this like statement expressions, in that it doesn't diagnose the elision of a deleted constructor call. */ conv_context |= CCO_STMT_EXPR_RESULT; } /* if */ prep_elision_initializer_operand(operand, unqual_type, /*fill_in_dtor=*/TRUE, conv_context, ec_no_error, (a_boolean*)NULL, &dip); an_expr_node_ptr temp_init_expr; temp_init_expr = alloc_temp_init_node(operand->type, dip, /*is_lvalue=*/FALSE, /*is_explicit_cast=*/FALSE); make_lvalue_or_rvalue_expression_operand(temp_init_expr, operand); } else { temp_init_from_operand_full(operand, /*temp_type=*/NULL, /*result_is_lvalue=*/FALSE); } /* if */ } /* if */ } /* if */ } /* temp_init_from_comma_operand */ static a_boolean optimizable_class_rvalue_question( an_operand *operand_2, an_operand *operand_3, a_dynamic_init_ptr *dip_2, a_dynamic_init_ptr *dip_3) /* operand_2 and operand_3 are the second and third operands of a "?" operation that returns a class prvalue. Check to see whether a copy elision optimization can be applied, and return TRUE if so, with *dip_2 and *dip_3 set to point to the underlying dynamic initialization entries for the operands. */ { a_boolean optimizable = FALSE, op2_is_temp_init, op3_is_temp_init; an_expr_node_ptr op_2 = NULL, op_3 = NULL; *dip_2 = *dip_3 = NULL; /* If an operand is a comma operand, attempt to turn it into a temp-init. */ temp_init_from_comma_operand(operand_2); temp_init_from_comma_operand(operand_3); op2_is_temp_init = operand_is_temp_init_full(operand_2, &op_2); op3_is_temp_init = operand_is_temp_init_full(operand_3, &op_3); /* If only one of the operands is a temp-init, make a copy of the other one so both will be temp-inits and the optimization can be done. The is_class_struct_union_type tests guard against operands that are throws. */ if (op2_is_temp_init && !op3_is_temp_init && is_class_struct_union_type(operand_3->type)) { temp_init_from_operand_full(operand_3, operand_3->type, /*result_is_lvalue=*/FALSE); op3_is_temp_init = operand_is_temp_init_full(operand_3, &op_3); } else if (!op2_is_temp_init && op3_is_temp_init && is_class_struct_union_type(operand_2->type)) { temp_init_from_operand_full(operand_2, operand_2->type, /*result_is_lvalue=*/FALSE); op2_is_temp_init = operand_is_temp_init_full(operand_2, &op_2); } /* if */ if (op2_is_temp_init && op3_is_temp_init) { check_assertion(op_2->is_lvalue == op_3->is_lvalue && op_2->is_xvalue == op_3->is_xvalue); /* Both are temporaries. The optimization can be done. */ optimizable = TRUE; *dip_2 = op_2->variant.init.dynamic_init; *dip_3 = op_3->variant.init.dynamic_init; } /* if */ return optimizable; } /* optimizable_class_rvalue_question */ static void do_class_rvalue_question_optimization(a_dynamic_init_ptr dip_2, a_dynamic_init_ptr dip_3, an_operand *result) /* Do the copy elision optimization for a class prvalue "?" operation. optimizable_class_rvalue_question has previously determined that the optimization can be done. dip_2 and dip_3 point to the dynamic initializations for the temporaries of the operands of the "?", and result is the "?" operation on those operands, without the final temporary initialization. The necessary initialization is added and result is updated. */ { an_expr_node_ptr expr, temp_init_node; a_dynamic_init_ptr dip; /* In the optimized form, the temporary that is the result of the overall "?" is a dik_expression marked with class_rvalue_initialized_through_master_entry, which means the expression pointed to should be evaluated for its effects (mainly, initializing the top-level temporary) but its value should not be stored into the temporary. The initializations for the temporaries for each operand are linked to the master entry so that they will initialize its temporary (i.e., the operand initializations become dependent entries that do not have temporaries of their own), and any destructions indicated are removed (because they will be done by the master entry). */ /* Unlink the destructions, as they will not be used. */ remove_from_destruction_list(dip_2); remove_from_destruction_list(dip_3); dip_2->destructor = NULL; dip_3->destructor = NULL; dip_2->inside_conditional_expression = FALSE; dip_3->inside_conditional_expression = FALSE; if (strict_ansi_mode && !mandatory_copy_elision) { /* In strict mode, issue an error if the copy constructor that would have been called is not accessible. */ handle_elided_copy_constructor(result->type, /*elided_cctor=*/(a_routine *)NULL, &result->position); } /* if */ /* Make the enk_temp_init node and the dynamic-init entry under it. */ temp_init_node = create_expr_temporary(result->type, /*is_lvalue=*/FALSE, /*is_explicit_cast=*/FALSE, /*suppress_abstract_test=*/TRUE, (a_dynamic_init_kind)dik_expression, &result->position, &dip); dip->class_rvalue_initialized_through_master_entry = TRUE; expr = make_node_from_operand(result); set_expr_result_not_used(expr); dip->variant.expression = expr; make_expression_operand(temp_init_node, result); /* Link the initializations for the operands to the result initialization so they will use the same temporary. */ dip_2->master_entry = dip; dip_3->master_entry = dip; } /* do_class_rvalue_question_optimization */ void do_question_operation(an_operand *operand_1, an_operand *operand_2, an_operand *operand_3, a_type_ptr result_type, a_boolean result_is_an_lvalue, a_boolean suppress_class_rvalue_temp, a_boolean suppress_class_temp_optimization, a_boolean template_case, a_boolean is_gnu_two_operand_form, a_source_position *question_position, a_source_position *colon_position, an_operand *result) /* Build an operand for a "?" operation. operand_1, operand_2, and operand_3 are the operands. result_type is the result type. The result is an lvalue if result_is_an_lvalue is TRUE (note that for xvalue cases the result is built here as an lvalue and then changed by the caller to an xvalue, so result_is_an_lvalue will be passed in TRUE). The operand is built in *result. Constant operations are folded if appropriate. suppress_class_rvalue_temp is TRUE if the generation of an extra temporary for the result of a class rvalue case should be suppressed. suppress_class_temp_optimization is TRUE if no attempt should be made to eliminate an unneeded temporary copy for a class rvalue result. template_case is TRUE if this is a template-dependent case. is_gnu_two_operand_form is TRUE if this is a GNU two-operand "?" (a synthesized operand_2 is still provided). question_position and colon_position give the position of the "?" and ":". */ { a_boolean operand_1_is_const, do_folding = FALSE, template_constant; a_type_ptr operation_type; a_boolean class_rvalue_case = FALSE; a_boolean class_rvalue_cctor_case = FALSE; a_boolean optimizable = FALSE; a_dynamic_init_ptr dip_2 = NULL, dip_3 = NULL; an_operand *preserved_operand = NULL, *discarded_operand = NULL; operation_type = result_type; if (!result_is_an_lvalue) { /* Cases that return a class rvalue have to be handled specially: an extra copy to a temporary is needed at the end. */ if (!C_mode() && is_class_struct_union_type(result_type)) { class_rvalue_case = TRUE; } /* if */ } /* if */ template_constant = (!C_mode() && (template_case || is_template_param_constant_operand(operand_1) || is_template_param_constant_operand(operand_2) || is_template_param_constant_operand(operand_3)) && (is_constant_operand(operand_1) && is_constant_operand(operand_2) && is_constant_operand(operand_3))); /* template_case is TRUE only for cases involving template-dependent types, so it might not be set if an operand is template-dependent but has a known type. */ if (template_constant) template_case = TRUE; /* If the first operand is a known constant, the operation can be folded. */ operand_1_is_const = is_constant_operand(operand_1) && constant_bool_value_known_at_compile_time( &operand_1->variant.constant); if (operand_1_is_const) { /* Decide whether we want to fold. We must fold in cases where the result must be a constant expression (or could be one), but in other cases we don't. In some of those other cases we don't fold because we can't easily do so (for example, if there were destructible objects attached to the second or third operand). */ if (op_is_false_constant(operand_1)) { /* The first operand is false; the third operand is the result. */ preserved_operand = operand_3; discarded_operand = operand_2; } else { /* The first operand is true; the second operand is the result. */ preserved_operand = operand_2; discarded_operand = operand_3; } /* if */ if (!is_constant_operand(preserved_operand)) { /* Only fold if we can produce a constant. Otherwise, there'll be no way to preserve the original form of the expression as a backing expression. */ do_folding = FALSE; } else if (template_case) { /* Don't fold template-dependent cases. If the operation is constant the expression will be placed under a ck_template_param constant below. */ do_folding = FALSE; } else if (expr_stack->is_type_operator_arg_expression) { /* Don't fold if the current expression is a decltype operand, where for something like x ? f1() : f2() returning f1() would cause decltype to return the declared return type of f1, which might be different than the type of f1() (e.g., cv-qualifiers might be different). */ do_folding = FALSE; } else if (!identical_types(operand_2->type, operand_3->type)) { /* Can't fold cases where the operand types do not match (e.g., because one is a throw and the other is not). */ do_folding = FALSE; } else if (curr_expr_kind_is_const()) { /* In constant expressions we must always fold. */ do_folding = TRUE; } else if (is_constant_operand(operand_2) && is_constant_operand(operand_3)) { /* Fold if the second and third operands are constants. */ do_folding = TRUE; } else if (class_rvalue_case) { /* Don't fold when the result is a class rvalue, because a copy is required. */ do_folding = FALSE; } else if (gnu_mode && is_expression_operand(discarded_operand) && has_statement_expression( discarded_operand->variant.expression)) { /* GNU statement expressions may give rise to scopes, which are too expensive to eliminate. */ do_folding = FALSE; } else if (!(operand_2->ruled_out_expr_kinds & ROEK_CONSTANT) && !(operand_3->ruled_out_expr_kinds & ROEK_CONSTANT)) { /* Fold if all the operands have the form of a constant expression. This deals with cases like 1 ? 1 : 1/0, in which the last operand would not be in constant form because it couldn't be folded. */ do_folding = TRUE; } else if (!strict_ansi_mode && expr_stack->favor_constant_result) { /* If we'd prefer a constant result, fold. */ do_folding = TRUE; } /* if */ } /* if */ if (do_folding) { /* The first operand is a constant. Fold the operation to the second or third operand. */ a_ruled_out_expr_kind_set saved_ruled_out_expr_kinds; copy_operand(preserved_operand, result); result->is_simple_string_literal = FALSE; result->is_cfront_null_pointer_constant = FALSE; result->is_id_expression = FALSE; result->id_expression_was_parenthesized = FALSE; /* In C and C++03, the unevaluated part of the operation can still disqualify it from being a constant-expression or a null pointer constant. The constant-expression part is only done in strict mode, however. */ saved_ruled_out_expr_kinds = result->ruled_out_expr_kinds; preserve_ruled_out_expr_kinds_from_discarded_operand(discarded_operand, result); if (!strict_ansi_mode) { result->ruled_out_expr_kinds = saved_ruled_out_expr_kinds; } /* if */ /* operand_1 is also discarded. */ preserve_ruled_out_expr_kinds_from_discarded_operand(operand_1, result); if (is_constant_operand(result)) { break_source_corresp(&result->variant.constant.source_corresp); if (curr_expr_kind_is_one_in_which_const_exprs_are_recorded()) { /* Create an expression to be recorded in the constant. */ an_operand result_expr; build_question_result_operand(operand_1, operand_2, operand_3, operation_type, result_is_an_lvalue, is_gnu_two_operand_form, &result_expr); check_assertion(is_expression_operand(&result_expr) && is_an_lvalue(&result_expr) == is_an_lvalue(result)); result->variant.constant.expr = result_expr.variant.expression; } else { if (is_constant_operand(discarded_operand)) { eliminate_stmt_expr_refs_if_constant_dropped( &discarded_operand->variant.constant); } /* if */ if (is_constant_operand(operand_1)) { eliminate_stmt_expr_refs_if_constant_dropped( &operand_1->variant.constant); } /* if */ } /* if */ #if BACK_END_IS_CP_GEN_BE } else if (is_expression_operand(result)) { /* Force the C++-generating back end to keep an implicit cast, reference indirection, or enk_temp_init at the top of this expression. */ an_expr_node_ptr expr = result->variant.expression; /* No skip_parens needed here; the implicit cast is on top of everything else because it's compiler-generated. */ if ((is_operation_node(expr) && (is_cast_operation_node(expr) || node_operator_is(expr, eok_ref_indirect) || expr->variant.operation.is_conversion_call) && expr->compiler_generated) || expr->kind == (an_expr_node_kind)enk_temp_init) { expr->keep_as_cast_for_cp_gen_be = TRUE; } /* if */ #endif /* BACK_END_IS_CP_GEN_BE */ } /* if */ } else if (is_error_operand(operand_1) || is_error_operand(operand_2) || is_error_operand(operand_3)) { /* Some error. */ make_error_operand(result); } else if (curr_expr_kind_is_evaluated_const() && !constexpr_enabled && !operand_1_is_const && !is_template_dependent_context()) { /* A constant expression expected by the value of the first operand is not known at compile time: Issue an error. Don't issue an error for a template-dependent value. If constexpr evaluation is in effect, a constant is not always required at this level (the enclosing expression may be found to be a valid constant instead). */ error_in_operand(ec_constant_value_not_known, operand_1); make_error_operand(result); } else { if (suppress_class_rvalue_temp) { class_rvalue_case = FALSE; } else if (microsoft_mode && rvalue_references_enabled && !ms_strict_ternary) { /* MSVC10 treats an xvalue as similar to an lvalue and doesn't copy it. */ if (is_an_xvalue(operand_2) && is_an_xvalue(operand_3)) { class_rvalue_case = FALSE; } /* if */ } /* if */ if (class_rvalue_case) { /* When the result is a class rvalue, we will do a final copy. */ /* See if this operation can be optimized to avoid a copy. */ if (optimizable_class_rvalue_question(operand_2, operand_3, &dip_2, &dip_3)) { optimizable = TRUE; } else { if (!symbol_supplement_for_class(result_type)-> construction_by_bitwise_copy_allowed) { /* A copy constructor will be used to do the final copy, so the operands of the "?" are the addresses of the class objects. The class test here is needed for mixed throw/class cases. */ class_rvalue_cctor_case = TRUE; if (is_class_struct_union_type(operand_2->type)) { conv_class_prvalue_operand_to_lvalue(operand_2); operation_type = operand_2->type; } /* if */ if (is_class_struct_union_type(operand_3->type)) { conv_class_prvalue_operand_to_lvalue(operand_3); operation_type = operand_3->type; } /* if */ } /* if */ } /* if */ } /* if */ /* Build the expression tree for the operation. */ build_question_result_operand(operand_1, operand_2, operand_3, operation_type, (result_is_an_lvalue || class_rvalue_cctor_case), is_gnu_two_operand_form, result); if (!C_mode()) { if (class_rvalue_case) { /* Give the operand a position so it can be used as an error position if necessary. */ result->position = operand_2->position; if (optimizable) { /* Do the copy optimization. */ if (!suppress_class_temp_optimization) { do_class_rvalue_question_optimization(dip_2, dip_3, result); } else { dip_2->inside_conditional_expression = TRUE; dip_3->inside_conditional_expression = TRUE; } /* if */ } else { /* For the unoptimized class rvalue case, make an extra copy, producing a single temporary result for the whole operation. */ a_type_ptr temp_type = skip_typerefs(result->type); if (is_immediate_class_type(temp_type)) { /* Preserve qualifiers on class-type prvalues. */ temp_type = result->type; } /* if */ temp_init_from_operand_full(result, temp_type, /*result_is_lvalue=*/FALSE); } /* if */ if (!is_error_operand(result) && !(optimizable && suppress_class_temp_optimization)) { /* Mark the initialization as being for a class rvalue "?". */ check_assertion(is_expression_operand(result) && result->variant.expression->kind == (an_expr_node_kind)enk_temp_init); result->variant.expression->variant.init.dynamic_init-> is_result_for_class_rvalue_question_mark = TRUE; } /* if */ } /* if */ if (template_constant) { /* For an expression based on a template parameter, scanned during the prototype instantiation, make a ck_template_param constant for the result. */ make_template_param_expr_constant_operand(result); } /* if */ if (result_is_an_lvalue) { /* Adjust the operand to make it an lvalue. */ set_glvalue_operand_state(result); } /* if */ } /* if */ } /* if */ if (!do_folding) { result->ruled_out_expr_kinds = (operand_1->ruled_out_expr_kinds | operand_3->ruled_out_expr_kinds); result->ruled_out_expr_kinds |= operand_2->ruled_out_expr_kinds; } /* if */ record_operator_position_in_rescan_info(result, question_position, NO_TOKEN_SEQUENCE_NUMBER, colon_position); } /* do_question_operation */ void template_question_operation(an_operand *operand_1, an_operand *operand_2, an_operand *operand_3, a_boolean is_gnu_two_operand_form, a_source_position *question_position, a_source_position *colon_position, an_operand *result) /* This routine is a wrapper for do_question_operation for the case where an expression is being built from operands whose types are based on template parameter types. When the current expression kind is constant, this can happen in nontype template arguments. Otherwise, it happens in prototype instantiations. is_gnu_two_operand_form is TRUE if this is a GNU two-operand "?" (a synthesized operand_2 is still provided). question_position and colon_position give the positions of the "?" and ":" operators. */ { if (curr_expr_kind_is_const()) { do_constant_generic_operand_transformations(operand_1); do_constant_generic_operand_transformations(operand_2); do_constant_generic_operand_transformations(operand_3); } else { /* The current expression is not a constant expression. */ prep_generic_operand(operand_1); prep_generic_operand(operand_2); prep_generic_operand(operand_3); } /* if */ do_question_operation(operand_1, operand_2, operand_3, type_of_unknown_templ_param_nontype, /*result_is_an_lvalue=*/FALSE, /*suppress_class_rvalue_temp=*/TRUE, /*suppress_class_temp_optimization=*/FALSE, /*template_case=*/TRUE, is_gnu_two_operand_form, question_position, colon_position, result); } /* template_question_operation */ static a_boolean potential_gnu_ignored_object_expr(an_expr_node_ptr obj_expr, a_token_kind tok) /* In C++11 mode, g++ allows a non-constant object expression in a member access expression appearing in a constant expression context if the expression has no side effects and the member is a constant. Return TRUE if we are in g++ (but not clang) C++11 mode, obj_expr has no side effects, and tok, the token following the expression, is either "." or "->" so that the error that would normally be issued for this case can be suppressed. (An error will be reported later if the member is not constant.) */ { a_boolean result = FALSE; if (gpp_mode && cpp11_mode && !clang_mode && (tok == tok_period || tok == tok_arrow) && !node_has_side_effects(obj_expr, (a_boolean *)NULL)) { /* Return TRUE so no error will be reported until the kind of the member is known. */ result = TRUE; } /* if */ return result; } /* potential_gnu_ignored_object_expr */ void add_reference_indirection(an_operand *result) /* *result has a C++ reference (or C++/CLI tracking reference) type; add an implicit indirection to it. *result may be an rvalue or an lvalue on input; on output it will be an lvalue. */ { an_expr_node_ptr node; an_operand orig_result; check_assertion_or_expect_error_str( is_any_reference_type(result->type), "add_reference_indirection: not reference type"); if (curr_expr_kind_is_traditional_const() && !current_mode_allows_field_selection_folding()) { /* Can't do reference indirection in a constant expression. This is needed in particular for ek_init_constant expressions out of scan_extended_integral_constant_expression. */ /* In certain modes we allow this so that field selections like x.y, where x is a reference and y is a constant, can be considered constant expressions. There will still be a check later that the result of the constant expression is constant. */ error_in_operand(ec_expr_not_constant, result); } else { a_variable_ptr var; orig_result = *result; if (is_constant_operand(result) && con_is_exact_addr_of_variable(&result->variant.constant, &var, /*array_decay_allowed=*/FALSE)) { /* The original operand is the exact address of a variable, so the result is an lvalue for that variable. */ node = var_lvalue_expr(var); } else { node = make_node_from_operand(result); if (is_an_lvalue(result)) { a_boolean constant_addr, *p_constant_addr; /* Convert from an lvalue for the reference to a prvalue for the value of the reference (in effect, loading the reference pointer value from the location that contains it). */ if (is_variable_node(node)) { a_variable_ptr vp = node_variable(node); if (vp->initializer_in_class && vp->is_template_variable) { ensure_inclass_static_member_constant_initializer_is_scanned(vp); } /* if */ if (vp->init_kind == (an_init_kind)initk_static && curr_expr_is_potentially_evaluated()) { /* The reference variable has a known-value initializer and the value of that initializer may be used (i.e., we're not just interested in the type of the reference or the size of what it references). */ p_constant_addr = &constant_addr; } else { /* The address may be "constant", but we don't want to fold it. */ p_constant_addr = (a_boolean*)NULL; constant_addr = var_has_static_storage_duration(vp); } /* if */ } else { p_constant_addr = &constant_addr; } /* if */ node = conv_glvalue_expr_to_prvalue(node, p_constant_addr, (a_constant_ptr *)NULL, &result->position); /* Change the references to "use". */ change_some_ref_kinds(result->ref_entries_list, SRK_REFERENCE, SRK_USE); } /* if */ /* Add a reference indirection to make an lvalue. This is similar to adding a "*" operator on top of a pointer prvalue. */ node = add_ref_indirection_to_node(node); node->position = result->position; } /* if */ make_glvalue_expression_operand(node, result); /* Restore the original source position, etc. Note that the reference entries are NOT restored, on purpose. */ restore_operand_details(result, &orig_result); result->ref_entries_list = NULL; } /* if */ /* A reference indirection rules out a constant expression. */ rule_out_expr_kinds(ROEK_CONSTANT, result); } /* add_reference_indirection */ static an_expr_node_ptr expr_to_record_for_variable(a_variable_ptr variable, a_boolean is_lvalue) /* Return the expression to be recorded in the "expr" field of a constant. The expression is a reference to the indicated variable, and an lvalue if is_lvalue is TRUE. Return NULL if the expression cannot be generated. */ { an_expr_node_ptr expr = NULL; if (curr_expr_kind_is_one_in_which_const_exprs_are_recorded()) { if (!in_file_scope(variable) && curr_il_region_number == file_scope_region_number) { /* Can't record a local variable in a file-scope expression. This, e.g., comes up in constant expressions (like array bounds) that reference const-valued local variables. */ check_assertion(curr_expr_kind_is_const() || curr_expr_kind_is(ek_sizeof) || expr_stack->is_template_arg_expression || expr_stack->is_vla_dimension_expression || variable->constant_valued); } else if (variable->is_compound_literal) { /* The constant for this variable is the variable's initializer, so avoid a circular reference by recording no expression. */ } else { expr = is_lvalue ? var_lvalue_expr(variable) : var_rvalue_expr(variable); } /* if */ } /* if */ return expr; } /* expr_to_record_for_variable */ #if GNU_EXTENSIONS_ALLOWED a_boolean operand_is_address_of_label(an_operand *op) /* Return TRUE if the given operand represents the address of a label (a GNU extension). */ { return is_constant_operand(op) && constant_is_address_of_label(&op->variant.constant); } /* operand_is_address_of_label */ #endif /* GNU_EXTENSIONS_ALLOWED */ static a_boolean is_register_variable(a_variable_ptr variable) /* Return TRUE if the indicated variable has some kind of register attribute, whether the standard C/C++ register storage class, a GNU register name, or an embedded C register name. */ { a_boolean is_reg = FALSE; if (variable->storage_class == (a_storage_class)sc_register) { is_reg = TRUE; #if GNU_EXTENSIONS_ALLOWED } else { if (variable->asm_name_is_valid) { /* variable->asm_name_or_reg.name, if non-NULL, indicates an asm name for the variable, but that's not a register name. */ } else { /* A register variable (GNU or Embedded C named) has no address. */ is_reg = TRUE; } /* if */ #endif /* GNU_EXTENSIONS_ALLOWED */ } /* if */ return is_reg; } /* is_register_variable */ a_boolean variable_has_constant_address(a_variable_ptr variable) /* Return TRUE if the indicated variable has a constant address. A static variable, for example, has a constant address, whereas a local auto or thread_local variable does not. */ { a_boolean const_addr = var_has_static_storage_duration(variable); #if MICROSOFT_EXTENSIONS_ALLOWED if ((variable->decl_modifiers & DM_DLLIMPORT) != 0 && C_mode() && !is_array_type(variable->type)) { /* A dllimport variable is accessed indirect through a variable and therefore does not have a constant address. */ const_addr = FALSE; } else if (cli_or_cx_enabled && variable->source_corresp.is_class_member && is_immediate_managed_class_type(parent_class_of(variable))) { /* Static data members of managed classes are allocated on the managed heap and therefore do not have constant addresses. */ const_addr = FALSE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if MICROSOFT_EXTENSIONS_ALLOWED || THREAD_LOCAL_STORAGE_SPECIFIER_ALLOWED if (const_addr && (variable->decl_modifiers & DM_THREAD) != 0) { const_addr = FALSE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED || THREAD_LOCAL_STORAGE_SPECIFIER_... */ if (const_addr && C_mode() && is_register_variable(variable)) { /* A register variable has no address in C. (In C++, you can take the address of a register variable.) */ const_addr = FALSE; } /* if */ return const_addr; } /* variable_has_constant_address */ void make_lvalue_variable_operand(a_variable_ptr variable, a_source_position *position, ARG_UNUSED a_source_position *end_position, an_operand *result, a_ref_entry_ptr rep) /* Make an lvalue operand for a variable. The source position of the operand is set to *position, and its end position, if any, is set to *end_position. rep points to an associated reference entry, or is NULL if none is needed. */ { an_expr_node_ptr node; a_type_ptr variable_type = variable->type; if ((strict_ansi_mode || !C_mode()) && is_void_type(variable_type) && !is_qualified_type(variable_type)) { /* If the variable has type void, make an rvalue instead of an lvalue. See ANSI C 3.2.2.1. This helps with extern void x; x; &x; Do this only in C++ and strict C mode. */ make_expression_operand(var_rvalue_expr(variable), result); } else { node = var_lvalue_expr(variable); make_glvalue_expression_operand(node, result); /* Start a list of reference entries related to the operand. */ result->ref_entries_list = rep; /* If the variable has a reference type, add an implicit indirection. */ if (!C_mode() && is_any_reference_type(variable_type)) { add_reference_indirection(result); } /* if */ } /* if */ result->position = *position; #if EXTRA_SOURCE_POSITIONS_IN_IL result->end_position = *end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* Set the position in the expression too. */ set_operand_expr_position_if_expr(result, (a_source_position *)NULL); } /* make_lvalue_variable_operand */ a_boolean is_potentially_constant_valued_variable(a_variable_ptr var) /* Return TRUE if var is a constant variable potentially usable in constant expressions. Such a variable has non-volatile const integral or enum type, and possibly some other const types in some modes. In a prototype instantiation, it could instead have a template parameter type. The other half of this check, that the variable has an initializer, is done in var_constant_value[_full]. */ { a_boolean is_const = FALSE; a_type_ptr var_type = var->type; if (((is_integral_or_enum_type(var_type) || (gcc_version_is(>=80000) && gcc_const_variables_allowed)) && is_const_qualified_type(var_type)) || (is_template_param_type(var_type) && (!is_auto_type(var_type) || is_decltype_auto_type(var_type)))) { is_const = TRUE; } else if (var->is_constexpr && is_literal_type(var_type)) { /* C++11 also allows other literal types (e.g., classes) if the variable is declared "constexpr". */ is_const = TRUE; } else if (constexpr_enabled && is_any_reference_type(var_type)) { /* C++11 also allows reference-typed variables (they're essentially "const" always). */ is_const = TRUE; #if GNU_EXTENSIONS_ALLOWED } else if (gpp_mode && is_scalar_type(var_type) && is_const_qualified_type(var_type)) { /* GCC allows const scalar expressions (specifically, floating-point and pointer constants). */ is_const = TRUE; } else if ((gcc_version_is(>=80000) || clangc_version_is(>=170000)) && is_const_qualified_type(skip_array_types(var_type))) { /* In C mode, recent versions of GCC and Clang fold variables of just about any const-qualified type. For example: int const arr[] = { 0, 1 }; struct S { int i; } S s = { arr[1] }; is accepted by some GCC and Clang versions. */ is_const = TRUE; #endif /* GNU_EXTENSIONS_ALLOWED */ } /* if */ if (is_const && is_volatile_qualified_type(var_type)) { /* A volatile-qualified variable can't be treated as constant-valued. */ is_const = FALSE; } /* if */ return is_const; } /* is_potentially_constant_valued_variable */ a_constant_ptr var_constant_value_full(a_variable_ptr var, a_boolean copy_for_reuse, a_boolean clear_backing_expr, a_boolean allow_C_mode_const_var) /* If the variable var has a constant initial value, return a pointer to it; otherwise, return NULL. A variable with an aggregate initial value is considered to have no initial value. Also, a variable with an address- constant initial value is treated as having a nonconstant initial value. If copy_for_reuse is TRUE, the constant returned will be a copy that can be reused by putting it in an_operand entry and later allocating it; when copy_for_reuse is FALSE, the copy returned can be examined but it should be considered read-only and shouldn't be allowed to survive beyond the short term. If copy_for_reuse is TRUE and clear_backing_expr is also TRUE, the backing expression pointer will be cleared, if necessary, by copying the constant (otherwise, a copy will not be done merely to clear the backing expression). If allow_C_mode_const_var is TRUE, a value will be returned for a C mode const variable. */ { a_constant_ptr con_val = NULL; an_init_kind init_kind; an_initializer_ptr init; if (var->is_template_variable && var->is_constexpr && !var->is_prototype_instantiation && !var->is_nonreal && var->init_kind == (an_init_kind)initk_none) { /* A variable template instance: Make sure the initializer is instantiated if needed. In contexts where diagnostics are suppressed (due to rescanning), avoid requesting the instantiation if that instantiation is already under way (indicating unbounded recursion): Instead end the recursion and record a rescanning failure. */ a_symbol_ptr var_sym = symbol_for(var); a_template_instance_ptr tip; tip = symbol_is(var_sym, sk_variable) ? var_sym->variant.variable.instance_ptr : var_sym->variant.static_data_member.instance_ptr; if (!tip->instantiation_requested_for_constant_value || expr_stack == NULL || !expr_stack->suppress_diagnostics) { tip->instantiation_requested_for_constant_value = TRUE; set_instance_required(var_sym, TRUE, SIR_NONE); } else { record_suppressed_error(); } /* if */ } /* if */ if (var->source_corresp.is_class_member && var->init_kind == (an_init_kind)initk_none && is_potentially_constant_valued_variable(var)) { /* In some modes, some in-class initializers for static data members are not scanned immediately. This is not done for variable template instances (i.e., when template_arg_list is non-NULL). */ if (gpp_mode && var->is_template_variable && var->template_info->template_arg_list == NULL && !var->is_prototype_instantiation) { /* GNU and Clang static data member initializers are sometimes parsed late. */ ensure_inclass_static_member_constant_initializer_is_scanned(var); #if MICROSOFT_EXTENSIONS_ALLOWED } else if (cli_or_cx_enabled && is_immediate_managed_class_type(parent_class_of(var))) { /* A static data member of a managed class type. The scanning of its in-class initializer (if any) is delayed by default until the complete class definition has been seen. However, if the member is used in the context of a constant-expression, its initializer is scanned as such at that point. */ ensure_inclass_static_member_constant_initializer_is_scanned(var); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ } /* if */ /* See if the variable has a known constant value. */ if (gnu_mode && var->is_compound_literal) { /* In GNU C and C++, a variable representing an lvalue for a compound literal can result from an expression like "*&(S){{0}}". Treat this as a constant. */ get_variable_initializer(var, (a_scope_ptr)NULL, &init_kind, &init); check_assertion(init_kind == (an_init_kind)initk_static); con_val = init->constant; } else if (microsoft_bugs && var->source_corresp.is_class_member && !var->is_constexpr && !var->is_member_constant && var->is_template_variable && !(cpp11_mode && !implicit_microsoft_cpp11_mode)) { /* The variable is a static data member but it's not initialized within the class (it might be initialized outside the class). Core issue 721 (which retroactively applies to C++03) clarified that the use of such a variable in a constant expression is permitted (if it meets the other requirements), but Microsoft compilers still disallow it for template static data member instantiations (as of MSVC 19.12.25715.). The Microsoft compiler does accept constexpr out-of-class partial specializations (but not out-of-class definitions) so to more closely match their behavior we accept out-of-class definitions if they are constexpr. */ } else if (var->is_expansion_iterator && scope_stack_top().in_expansion_statement_body) { /* A name introduced by the for-range-declaration of an expansion statement is value-dependent (N5050 [temp.dep.constexpr]/2.4). */ con_val = alloc_constant(ck_template_param); make_template_param_expr_constant(var_rvalue_expr(var), con_val); } else if ((!C_mode() || allow_C_mode_const_var) && is_potentially_constant_valued_variable(var)) { if (var->source_corresp.is_class_member && var->is_template_variable && !var->is_member_constant && !parent_class_of(var)->variant.class_struct_union.is_nonreal_class) { /* Instantiate a variable template instance or template static data member to get its definition (but don't force a definition in the object file). */ complete_template_variable_type_is_needed(var); } /* if */ /* initk_function_local initialization can come up with local static variables when recording a constant expression (the expression is function-local, and that forces the initializer constant to be made function-local as well). */ con_val = initializer_constant(var); if (con_val == NULL && gpp_mode && var->init_kind == initk_none && var->source_corresp.is_class_member && !var->is_member_constant && parent_class_of(var)->variant.class_struct_union.is_nonreal_class && is_template_dependent_type(var->type)) { /* g++ accepts a const static data member of a class template with no in-class initializer as a constant-expression during the prototype instantiation of its class. In a real instantiation, the out-of-class definition must be present. */ con_val = alloc_constant(ck_template_param); make_template_param_expr_constant(var_rvalue_expr(var), con_val); } else if (con_val == NULL && !constexpr_enabled && var->is_nonreal && var->init_kind == initk_none && var->template_info != NULL && var->template_info->template_arg_list != NULL) { /* A variable template instance with template-dependent arguments (e.g., "vt" in a template), whose value is not known until instantiation. A pre-C++11 constant expression (where variable templates are an extension) requires a constant here, so treat the reference as a template-dependent constant. */ con_val = alloc_constant(ck_template_param); make_template_param_expr_constant(var_rvalue_expr(var), con_val); } /* if */ if (!C_mode() && !constexpr_enabled && strict_ansi_mode && !var->constant_valued) { /* In strict C++ mode, there are some cases that look constant but don't have the right form, so pretend the initializer is not constant for those. */ con_val = NULL; } /* if */ if (con_val != NULL) { if (constexpr_enabled && (var->is_constexpr || is_any_reference_type(var->type))) { /* C++11 allows more kinds of constants for variables declared "constexpr" (e.g., class types) and for references. */ } else if (constant_is(con_val, ck_aggregate) && !(gcc_const_variables_allowed && (gcc_version_is(>=80000) || clangc_version_is(>=170000)))) { /* An aggregate cannot be considered a constant value in pre-C++11 code. In some GNU C and Clang C modes, however, they are usable as constant values. */ con_val = NULL; } else if (constant_is(con_val, ck_address)) { /* The address of a variable, routine, or string literal cannot generally be considered a constant value in pre-C++11 code. */ /* However, in g++ mode they are allowed because const variables initialized with scalars are considered constants (see is_potentially_constant_valued_variable). String literals are excluded so that back ends can distinguish cases specified as constants (for which back ends can assume that string literals have no identity, and use different constants for different uses of the same constant if that helps optimization) and cases specified as constant variables (for which back ends are required to use the same constant for every reference). */ if (!gnu_mode || (address_base_is(con_val, abk_constant) && constant_is(con_val->variant.address.variant.constant, ck_string))) { con_val = NULL; } /* if */ } /* if */ } /* if */ } /* if */ if (copy_for_reuse && con_val != NULL) { /* Make a copy that can be reused. */ a_constant_ptr new_con; an_expr_node_ptr backing_expr = con_val->expr; /* Clear the backing expression in the original constant so the copy will not have one. The pointer will be restored below. */ con_val->expr = NULL; if (con_val->kind == (a_constant_repr_kind)ck_template_param || con_val->kind == (a_constant_repr_kind)ck_aggregate) { /* For template parameter constants, do a deep copy because there might be an expression subtree. Likewise for aggregate constants. */ an_expr_copy_options_set options = CE_COPIED_CONSTANTS_MAY_BE_SHARED; if (!in_file_scope(con_val) && var->source_corresp.enclosing_routine != NULL && mem_region_for_routine(var->source_corresp.enclosing_routine) != curr_il_region_number) { options |= CE_COPYING_FROM_ONE_FUNC_TO_ANOTHER; } /* if */ new_con = copy_constant_full(con_val, (a_constant *)NULL, options); } else if (backing_expr != NULL && clear_backing_expr) { /* Copy to allow clearing the backing expression. This is a shallow copy. */ new_con = alloc_shareable_constant(con_val); } else { /* No copy needed. */ new_con = con_val; } /* if */ /* Restore the backing expression in the original constant. */ con_val->expr = backing_expr; con_val = new_con; } /* if */ return con_val; } /* var_constant_value_full */ a_constant_ptr var_constant_value(a_variable_ptr var) /* Interface to var_constant_value_full for the usual case. The constant returned should only be used locally and not linked into the IL tree. */ { a_constant_ptr con_val = var_constant_value_full( var, /*copy_for_reuse=*/FALSE, /*clear_backing_expr=*/FALSE, /*allow_C_mode_const_var=*/FALSE); return con_val; } /* var_constant_value */ static a_boolean enable_if_cond_is_true_constant(an_attribute_ptr ap) /* Return TRUE if the given enable_if attribute's condition expression can be folded to a constant of "true" value. If it can be folded at all, this may keep the folded result recorded in the attribute. */ { a_boolean result = FALSE; an_attribute_arg_ptr aap = ap->arguments; if (aap == NULL || aap->kind != aak_expression) { /* Something went wrong with scanning the attribute. */ } else { an_expr_node_ptr cond = expr_node_from_attribute_arg(aap); a_constant_ptr il_cp = NULL; if (is_constant_node(cond)) { il_cp = node_constant(cond); } else { a_constant_ptr cp = local_constant(); a_diag_list diag_list; if (interpret_expr(cond, /*is_constant_evaluated=*/TRUE, /*force_prvalue=*/TRUE, cp, &diag_list)) { /* The condition expression is unconditionally constant. Record the constant in the attribute to avoid repeating the interpretation in the future. */ a_memory_region_number region_to_switch_back_to; switch_to_file_scope_region(®ion_to_switch_back_to); il_cp = move_local_constant_to_il(&cp); aap->variant.expr = alloc_node_for_constant(il_cp); switch_back_to_original_region(region_to_switch_back_to); } /* if */ discard_more_info_list(&diag_list); release_local_constant(&cp); } /* if */ if (il_cp != NULL && !is_false_constant(il_cp)) { result = TRUE; } /* if */ } /* if */ return result; } /* enable_if_cond_is_true_constant */ static void require_true_enable_if_condition(a_type_ptr rtp, a_source_position *diag_pos) /* rtp is a routine type with one or more associated enable_if attributes. If any cannot be evaluated to a "true" value (because they cannot be evaluated at all, or because the value is "false") issue an error at diag_pos. */ { an_attribute_ptr ap; a_boolean issue_diag = FALSE; ap = find_attribute(ak_enable_if, rtp->source_corresp.attributes); check_assertion(ap != NULL); do { if (!enable_if_cond_is_true_constant(ap)) { issue_diag = TRUE; } /* if */ ap = find_attribute(ak_enable_if, ap->next); } while (ap != NULL); if (issue_diag) { expr_pos_error(ec_address_of_nontrue_enable_if_function, diag_pos); } /* if */ } /* require_true_enable_if_condition */ void make_ptr_to_member_constant_operand( a_symbol_ptr member_sym, a_symbol_ptr member_proj_sym, a_source_position *position, ARG_UNUSED a_source_position *end_position, a_boolean check_protected_access, a_boolean is_qualified_name, a_boolean has_required_ampersand, ARG_UNUSED a_boolean allow_on_managed, an_operand *result) /* Make an operand for a constant representing a C++ pointer to member. member_sym is the member (not overloaded, possibly a projection symbol). member_proj_sym is the same as member_sym, or is the overloaded function symbol that contains member_sym, or it can be a projection symbol for either of those. *position gives the source position to put into the operand. *end_position gives the end position if end positions are being maintained. If check_protected_access is TRUE and the symbol is a protected member, do the ARM 11.5 protected member access check. The name that generated this pointer-to-member constant is a qualified name if is_qualified_name is TRUE; it is the immediate operand of a "&" operator (with not even parentheses allowed to intervene) if has_required_ampersand is TRUE. If member_sym is a member of a C++/CLI managed class, issue an error unless allow_on_managed is TRUE. If the member is a bit field, issue an error. */ { a_symbol_ptr base_member_sym = fundamental_symbol_of(member_sym); a_constant_ptr constant = local_constant(); #if MICROSOFT_EXTENSIONS_ALLOWED if (cppcli_enabled && !allow_on_managed && is_managed_class_type(sym_parent_class(member_sym))) { /* You can't take a pointer-to-member of a member of a C++/CLI managed class. */ expr_pos_error(ec_ptr_to_member_of_managed_class, position); make_error_operand(result); goto done; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* The standard only allows this when a qualified name is preceded by a "&". In some modes we allow other forms as an extension. */ if (!has_required_ampersand || !is_qualified_name) { an_error_severity severity = (an_error_severity)es_discretionary_error; if (strict_ansi_mode) { /* The standard doesn't allow the nonstandard forms. */ severity = strict_ansi_discretionary_severity; } else if (gpp_mode) { /* g++ doesn't allow the nonstandard forms in any version (tested in 2.95 through 4.1). */ } else if (microsoft_mode) { /* MSVC++ allows the nonstandard forms in versions before 1400. */ if (microsoft_version < 1400) { severity = (an_error_severity)es_none; } /* if */ } else if (any_cfront_mode()) { /* Cfront allows the nonstandard forms in all versions. */ severity = (an_error_severity)es_none; } else if (sun_mode) { /* Sun allows the nonstandard forms in all versions (tested in Workshop 6 and Studio 11). */ severity = (an_error_severity)es_none; } /* if */ if (severity != (an_error_severity)es_none) { expr_pos_diagnostic(severity, ec_nonstd_member_function_address, position); } /* if */ } /* if */ /* Protected members of a base class can only be accessed through an object of a derived class (ARM 11.5). Post-ARM revisions have made it clear this applies also to pointers to members. We allow the address of a protected member to be taken as a member of the derived class but not as a member of the base class. For example: class A { protected: int i; }; class B : public A { void mf(); }; void B::mf() { int A::* pmi = &A::i; // error - protected member int B::* pmj = &B::i; // OK } Cfront does not do this checking, so we omit it in cfront mode. Also skip this check if it shouldn't be done (e.g., because an access control error has already been issued for the identifier). */ if (!any_cfront_mode() && check_protected_access #if MICROSOFT_EXTENSIONS_ALLOWED /* The Microsoft compiler does not do this check. */ && !ms_extensions #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ && expr_access_checking_should_be_done()) { a_boolean error_detected = FALSE; a_boolean *p_error_detected = NULL; /* If errors are suppressed, get a returned variable instead of issuing any error. */ if (expr_stack->suppress_diagnostics) p_error_detected = &error_detected; (void)check_protected_member_access(member_sym, member_proj_sym, position, sym_parent_class(member_proj_sym), p_error_detected); if (error_detected) record_suppressed_error(); } /* if */ /* No need to instantiate the class; since we have a member of it, it must be instantiated already. */ if (base_member_sym->kind == (a_symbol_kind)sk_field) { /* Pointer to nonstatic data member. */ a_field_ptr field = base_member_sym->variant.field.ptr; if (is_any_reference_type(field->type)) { if (expr_error_should_be_issued()) { pos_ty_error(ec_bad_member_type_in_ptr_to_member, position, field->type); } /* if */ make_error_operand(result); goto done; } else if (field->is_bit_field) { /* Cannot make a pointer-to-member of a bit field. */ expr_pos_error(ec_address_of_bit_field, position); } /* if */ set_ptr_to_data_member_constant(field, constant); } else { a_routine_ptr rout; a_type_ptr rtp; #if CHECKING if (base_member_sym->kind != (a_symbol_kind)sk_member_function) { internal_error("make_ptr_to_member_constant_operand: bad kind"); } /* if */ #endif /* CHECKING */ /* Pointer to nonstatic member function. */ rout = base_member_sym->variant.routine.ptr; if (rout->has_deducible_return_type && !rout->has_deduced_return_type) { finalize_deduced_return_type(rout, position); } /* if */ rtp = skip_typerefs(rout->type); if (rtp->kind == (a_type_kind)tk_routine && rtp->variant.routine.extra_info->has_enable_if_attribute) { require_true_enable_if_condition(rtp, position); } /* if */ set_ptr_to_member_function_constant(rout, constant); if (!rout->is_virtual) { /* Force the routine to be instantiated or generated. */ if_evaluating_mark_routine_referenced(rout); } /* if */ } /* if */ if (is_template_dependent_context() && is_template_dependent_type(constant->type)) { /* In a prototype instantiation, a member of the current class is template-dependent. */ force_constant_to_be_dependent(constant); } /* if */ make_constant_operand(constant, result); done: result->position = *position; #if EXTRA_SOURCE_POSITIONS_IN_IL result->end_position = *end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ release_local_constant(&constant); } /* make_ptr_to_member_constant_operand */ static void check_address_of_consteval_function(a_routine_ptr rp) /* The address of the given consteval routine is being taken but such an address can only be exposed in some limited contexts: - in an unevaluated context - in the definition of another consteval function - as part of a consteval call This last context can only be verified after the full expression has been processed (because we might not know the enclosing call until after overload resolution). Hence, such cases are handled elsewhere (see, e.g., the function diag_invalid_consteval_func_in_expr). For now, if the first two contexts do not apply, record the fact that we saw a reference to the given consteval routine. */ { if (!expr_stack->consteval_call_need_not_fold && curr_expr_is_potentially_evaluated()) { if (innermost_function_scope == NULL || !current_routine_entry()->is_consteval) { if (rp->is_deleted) { /* An error should have been issued already. An additional one is not really helpful. */ expect_error(); } else { expr_stack->consteval_function_designator_seen = TRUE; } /* if */ } /* if */ } /* if */ } /* check_address_of_consteval_function */ void make_function_designator_operand( a_symbol_ptr routine_sym, a_boolean is_qualified_name, ARG_UNUSED a_boolean compiler_generated, a_source_position *position, ARG_UNUSED a_source_position *end_position, a_ref_entry_ptr rep, an_operand *result) /* Make an operand for a function designator. routine_sym points to the routine symbol entry (not overloaded, but can be a projection symbol). is_qualified_name is TRUE if the function was named by a qualified name. compiler_generated is TRUE if this function reference is compiler-generated. The source position of the operand is set to *position (plus end_position, if end positions are being maintained). rep points to an associated reference entry, or is NULL if none is needed. */ { a_routine_ptr routine; an_expr_node_ptr node; reduce_projection_symbol_to_fundamental_symbol(routine_sym); #if CHECKING if (routine_sym->kind != (a_symbol_kind)sk_routine && routine_sym->kind != (a_symbol_kind)sk_member_function) { internal_error("make_function_designator_operand: sym not function"); } /* if */ #endif /* CHECKING */ routine = routine_sym->variant.routine.ptr; if (C_dialect == C_dialect_cplusplus && curr_expr_is_potentially_evaluated()) { if (routine == il_header.main_routine) { /* In C++, "main" cannot be called and cannot have its address taken. */ if (microsoft_mode || gpp_mode) { /* MSVC++ (up to 8.0 at least) and g++ (up to 3.4 at least) allow this. */ } else { expr_pos_error(ec_bad_use_of_main, position); } /* if */ } /* if */ } /* if */ if (expr_stack->suppress_diagnostics) { /* Catch some invalid situations that would otherwise result in actual errors later on. */ if (is_ineligible(routine_sym)) { record_suppressed_error(); make_error_operand(result); goto done; } else if (routine->has_deducible_return_type && !routine->has_deduced_return_type && !routine->is_prototype_instantiation && !routine->defined) { /* A function with a deducible return type that has not (yet) been deduced. If we are in a context where diagnostics are suppressed, treat the absence of a definition as a (suppressed) expression diagnostic rather than letting it be handled as an error "outside the immediate context" later on. */ a_boolean could_be_instantiated = FALSE; a_template_instance_ptr tip = routine_sym->variant.routine.instance_ptr; if (tip != NULL) { a_symbol_ptr tsym; tsym = prototype_template_if_template_symbol(tip->template_sym); if (tsym->defined) { could_be_instantiated = TRUE; } /* if */ } else if (routine_sym->variant.routine.ptr ->friend_defined_in_instantiation) { could_be_instantiated = TRUE; } /* if */ if (!could_be_instantiated) { record_suppressed_error(); make_error_operand(result); goto done; } /* if */ } /* if */ } /* if */ /* Make an expression for the function. */ node = function_lvalue_expr(routine); node->position = *position; make_glvalue_expression_operand(node, result); if (routine->source_corresp.is_class_member) { /* Remember whether or not the routine is virtual. Use of a qualified name suppresses the virtual-ness of the function. */ a_type_ptr class_type = parent_class_of(routine); result->virtual_function = routine->is_virtual && !is_qualified_name && !routine->final && !(is_immediate_class_type(class_type) && class_type->variant.class_struct_union.final); } /* if */ result->is_qualified_name = is_qualified_name; result->position = *position; #if EXTRA_SOURCE_POSITIONS_IN_IL result->end_position = *end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ if (!compiler_generated) { set_operand_expr_position_if_expr(result, (a_source_position *)NULL); } /* if */ /* Start a list of reference entries related to the operand. */ result->ref_entries_list = rep; /* If this is a non-virtual call, mark the routine entry as actually referenced. */ if (!result->virtual_function) { if_evaluating_mark_routine_referenced(routine); } else if (gpp_mode && rout_is_inline(routine) && curr_expr_is_potentially_evaluated()) { /* For g++, if the routine is inline and virtual, instantiate the statically-named virtual function even though it might not be the actual routine that will be called by the virtual call. */ set_instance_required(routine_sym, TRUE, SIR_NONE); } /* if */ if (routine->is_consteval) { /* This must wait until this point since the act of synthesizing the function might have turned a constexpr function into a consteval function. */ check_address_of_consteval_function(routine); } /* if */ #if USE_X86_FUNCTION_MULTIVERSIONING if (gpp_mode && is_multiversion_representative(routine)) { /* Record the use of a GNU function multiversion function (and report any errors). */ reference_to_mv_routine(routine, position); } /* if */ #endif /* USE_X86_FUNCTION_MULTIVERSIONING */ done:; } /* make_function_designator_operand */ void make_field_operand(a_symbol_locator *locator, a_source_position *source_position, ARG_UNUSED a_source_position *end_position, an_operand *result) /* Allocate an expression node to contain a field reference, and return an operand for it in *result. locator describes the field name reference. source_position and end_position give the starting and ending source positions of the field reference (end_position only in configurations with extra source positions). */ { an_expr_node_ptr node; a_symbol_ptr proj_field_sym = locator->specific_symbol; a_symbol_ptr field_sym = fundamental_symbol_of(proj_field_sym); a_field_ptr field; check_assertion(field_sym->kind == (a_symbol_kind)sk_field); field = field_sym->variant.field.ptr; /* Make the expression node. */ node = alloc_expr_node((an_expr_node_kind)enk_field); node->type = field->type; node_field(node) = field; /* Make the operand with the node. */ make_expression_operand(node, result); result->symbol = proj_field_sym; result->position = *source_position; #if EXTRA_SOURCE_POSITIONS_IN_IL result->end_position = *end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* Set the position in the expression too. */ set_operand_expr_position_if_expr(result, (a_source_position *)NULL); result->state = (an_operand_state)os_none; set_operand_id_details_from_locator(result, locator); } /* make_field_operand */ #if UPC_EXTENSIONS_ALLOWED void make_upc_thread_operand(an_operand *operand, a_constant_repr_kind kind) /* Make an operand for the THREADS or MYTHREAD pseudo-constants. kind can be ck_upc_threads or ck_upc_mythread. The position of the current token is used as the operand position. If the number of threads was specified on the command line and kind is ck_upc_threads, return an integer constant of the appropriate value instead. */ { check_assertion(kind == (a_constant_repr_kind)ck_upc_threads || kind == (a_constant_repr_kind)ck_upc_mythread); if (!upc_dynamic_threads() && kind == (a_constant_repr_kind)ck_upc_threads) { /* The number of threads was specified on the command line. Return an integer constant with the specified value. */ make_integer_constant_operand(operand, upc_num_threads); } else { a_constant *constant; /* The actual number of threads will be determined at run time. */ constant = alloc_constant(kind); constant->type = integer_type((an_integer_kind)ik_int); if (kind == (a_constant_repr_kind)ck_upc_threads) { /* ck_upc_threads represents an integer multiple of the dynamic THREADS value. Since THREADS == 1*THREADS, we set the integer_value variant field to one. */ set_integer_value(&constant->variant.integer_value, (a_host_large_integer)1); } else { /* ck_upc_mythread has no variant fields. */ } /* if */ clear_operand((an_operand_kind)ok_constant, operand); copy_constant(constant, &operand->variant.constant); operand->type = constant->type; operand->state = (an_operand_state)os_prvalue; set_operand_position_to_pos_curr_token(operand); } /* if */ } /* make_upc_thread_operand */ #endif /* UPC_EXTENSIONS_ALLOWED */ a_dynamic_init_ptr alloc_expr_dynamic_init(a_dynamic_init_kind kind) /* Allocate a dynamic initialization entry, clear it to default values, set its kind to kind, and return a pointer to it. This should be used instead of alloc_dynamic_init within the expression processing routines. It does some extra processing based on the knowledge that we are within an expression. */ { a_dynamic_init_ptr dip; /* Allocate and initialize the entry. */ dip = alloc_dynamic_init(kind); if (expr_stack->inside_conditional_expression) { dip->inside_conditional_expression = TRUE; } /* if */ return dip; } /* alloc_expr_dynamic_init */ a_dynamic_init_ptr alloc_expr_ctor_dynamic_init( a_routine_ptr ctor_routine, an_expr_node_ptr args, a_type_ptr dest_type, a_boolean static_temp, a_boolean add_default_args, a_boolean implied_source, a_boolean value_init, a_boolean sequenced_args, a_boolean fold_constexpr, a_boolean check_constexpr, a_source_position *pos) /* Allocate a dynamic initialization entry for a constructor call (dik_constructor), and return a pointer to it. ctor_routine gives the constructor (NULL for a dependent case). args gives the constructor argument list. dest_type is the destination type, if there is one (e.g., for a cast), or NULL otherwise. static_temp is TRUE if the static_temp field of the generated entry should be set to TRUE. add_default_args is TRUE if the expressions for any default arguments should be added to the end of the argument list. implied_source is TRUE if the call is a copy constructor call and the source for the copy is implied. value_init is TRUE if value- initialization is required. sequenced_args is TRUE if the arguments must be evaluated left-to-right. pos is the source position of the call (may be omitted if ctor_routine is NULL). This function does not fill in the destructor information, if any. If constexpr is enabled and fold_constexpr is TRUE the construction may be folded to a constant (a dik_constant dynamic init entry is returned). If check_constexpr is also TRUE, diagnostics are emitted in contexts requiring successful folding. */ { a_boolean folded = FALSE, checked = FALSE; a_dynamic_init_ptr dip = alloc_expr_dynamic_init(dik_constructor); dip->variant.constructor.ptr = ctor_routine; if (static_temp) { dip->static_temp = TRUE; } /* if */ if (implied_source) { dip->variant.constructor.is_copy_constructor_with_implied_source = TRUE; } /* if */ if (value_init) { dip->variant.constructor.value_initialization = TRUE; } /* if */ if (sequenced_args) { dip->variant.constructor.has_sequenced_arguments = TRUE; } /* if */ /* Add default arguments if any. */ if (add_default_args) { a_param_type_ptr ptp; an_expr_node_ptr def_args; check_assertion(ctor_routine != NULL); ptp = skip_typerefs(ctor_routine->type)->variant.routine.extra_info-> param_type_list; if (implied_source) ptp = ptp->next; { an_expr_node_ptr arg; /* Advance over the parameters for the arguments supplied. */ for (arg = args; arg != NULL; arg = arg->next) { if (ptp == NULL || ptp->is_parameter_pack) break; ptp = ptp->next; } /* for */ } def_args = expr_copy_default_arg_expr_list(ctor_routine, ptp); if (def_args != NULL) { /* Append the default argument list to the end of the arguments list. */ if (args == NULL) { args = def_args; } else { an_expr_node_ptr last; for (last = args; last->next != NULL; last = last->next) {} last->next = def_args; } /* if */ } /* if */ } /* if */ dip->variant.constructor.args = args; if (ctor_routine != NULL) { if (curr_expr_is_potentially_evaluated()) { ctor_routine->called = TRUE; } /* if */ if (ctor_routine->is_consteval) { fold_constexpr = TRUE; check_constexpr = TRUE; } /* if */ if (fold_constexpr && (ctor_routine->is_constexpr || ctor_routine->is_consteval)) { a_constant_ptr folded_con = local_constant(); check_assertion(pos != NULL); if (expr_fold_constexpr_ctor(ctor_routine, dip, pos, check_constexpr, folded_con)) { /* The constructor is declared constexpr and the construction has been folded to a constant. */ a_dynamic_init_ptr orig_dip = dip; folded = TRUE; if (dest_type != NULL) folded_con->type = dest_type; dip = alloc_expr_dynamic_init(dik_constant); set_dynamic_init_constant(dip, move_local_constant_to_il(&folded_con)); folded_con = dip->variant.constant.ptr; if (constant_is(folded_con, ck_aggregate) && folded_con->variant.aggregate.first_constant != NULL && !in_file_scope(folded_con) && in_file_scope(folded_con->variant.aggregate.first_constant)) { /* The constexpr interpreter creates aggregate sub-constants in file scope memory (because it doesn't know in what context the result will be used). If the current memory region is local, copy the result constant's substructure into that local memory. */ (void)copy_constant_full(folded_con, folded_con, CE_NO_OPTIONS); } /* if */ if (curr_expr_kind_is_one_in_which_const_exprs_are_recorded() || (exceptions_enabled && innermost_function_scope == NULL && ctor_routine != NULL && scope_stack_top().in_field_initializer)) { /* A backing expression is normally recorded only when curr_expr_kind_is_one_in_which_const_exprs_are_recorded() is TRUE. However, folding a constructor call to a bare constant in a default member initializer hides that call from a later noexcept determination: Since C++17 (P0003R5) such a call is potentially throwing even when it is a constant expression. This matters, e.g., for a class-type member's default member initializer that feeds the implicit exception specification of a defaulted default constructor. Retain the backing expression in that case as well. Doing so is limited to constructions outside of any function body, where the constant and the constructor call reside in the same (non-local) memory region and the expression can be attached directly. */ add_temp_init_backing_expression(folded_con, orig_dip); } /* if */ } else { release_local_constant(&folded_con); } /* if */ checked = check_constexpr; } /* if */ if (constexpr_enabled && !folded && !checked) { /* Construction was not folded to a constant. In a constant expression, that's an error. Pre-C++11 cases should be detected earlier. */ a_boolean prior_error = FALSE; an_expr_node_ptr arg; /* If an argument produced an error already, don't issue another diagnostic. */ for (arg = args; arg != NULL; arg = arg->next) { if (is_error_node(arg) || is_error_type(arg->type)) { prior_error = TRUE; if (!expr_stack->suppress_diagnostics) { expect_error(); } /* if */ break; } /* if */ } /* for */ check_assertion(pos != NULL); if (!prior_error && call_did_not_fold_to_constant(ctor_routine, (an_operand *)NULL, /*no_diagnostic=*/FALSE, (a_diag_list_ptr)NULL, pos)) { set_dynamic_init_kind(dip, (a_dynamic_init_kind)dik_constant); set_dynamic_init_constant(dip, alloc_error_constant()); } /* if */ } /* if */ if (ctor_routine->has_nodiscard_attribute) { /* Mark this expression for nodiscard processing. */ expr_stack->nodiscard_expr_seen = TRUE; } /* if */ } /* if */ return dip; } /* alloc_expr_ctor_dynamic_init */ a_routine_ptr expr_select_default_constructor(a_type_ptr class_type, a_source_position *err_pos, a_boolean *err) /* Interface to select_default_constructor_full for use within expression processing. Supplies some arguments from expression stack values. */ { a_routine_ptr ctor_routine; a_boolean error_detected = FALSE; a_boolean *p_error_detected = NULL; /* If errors are suppressed, get a returned variable instead of issuing any error. */ if (expr_stack->suppress_diagnostics) p_error_detected = &error_detected; ctor_routine = select_default_constructor_full(class_type, err_pos, class_type, /*declarative_context=*/FALSE, curr_expr_is_potentially_evaluated(), expr_access_checking_should_be_done(), /*no_explicit=*/FALSE, p_error_detected, err); if (error_detected) record_suppressed_error(); return ctor_routine; } /* expr_select_default_constructor */ a_routine_ptr expr_select_copy_constructor( a_type_ptr class_type, a_type_qualifier_set required_qualifiers, a_boolean source_is_rvalue, a_source_position *err_pos, a_boolean *class_bitwise_copy, a_boolean record_ref) /* Interface to select_copy_constructor_full for use within expression processing. Supplies some arguments from expression stack values. */ { a_routine_ptr cctor_routine; a_boolean error_detected = FALSE; a_boolean *p_error_detected = NULL; a_boolean check_access = (record_ref && expr_access_checking_should_be_done()); /* If errors are suppressed, get a returned variable instead of issuing any error. */ if (expr_stack->suppress_diagnostics) p_error_detected = &error_detected; cctor_routine = select_copy_constructor_full( class_type, required_qualifiers, source_is_rvalue, err_pos, class_type, class_bitwise_copy, record_ref, curr_expr_is_potentially_evaluated(), /*allow_suppressed_ctor=*/FALSE, check_access, p_error_detected); if (error_detected) record_suppressed_error(); return cctor_routine; } /* expr_select_copy_constructor */ a_routine_ptr expr_select_destructor_b(a_type_ptr class_type, a_type_ptr object_class_type, a_source_position *position, a_boolean honor_virtual, a_boolean *error_detected) /* Interface to select_destructor_full for use within expression processing. Supplies some arguments from expression stack values. If error_detected is non-NULL, no errors are issued and *error_detected is returned TRUE if there was an error. */ { a_routine_ptr dtor_routine; dtor_routine = select_destructor_full( class_type, object_class_type, position, honor_virtual, expr_stack != NULL && curr_expr_is_potentially_evaluated(), /*instantiate=*/ (error_detected == NULL) && (expr_stack == NULL || !expr_stack->is_default_arg_expression), expr_access_checking_should_be_done(), error_detected); return dtor_routine; } /* expr_select_destructor_b */ a_routine_ptr expr_select_destructor(a_type_ptr class_type, a_type_ptr object_class_type, a_source_position *position, a_boolean honor_virtual) /* Interface to select_destructor_full for use within expression processing. Supplies some arguments from expression stack values. */ { a_routine_ptr dtor_routine; a_boolean error_detected = FALSE; a_boolean *p_error_detected = NULL; /* If errors are suppressed, get a returned variable instead of issuing any error. */ if (expr_stack != NULL && expr_stack->suppress_diagnostics) { p_error_detected = &error_detected; } /* if */ dtor_routine = expr_select_destructor_b(class_type, object_class_type, position, honor_virtual, p_error_detected); if (error_detected) record_suppressed_error(); return dtor_routine; } /* expr_select_destructor */ void add_dtor_to_dynamic_init(a_dynamic_init_ptr dip, a_type_ptr class_type, a_type_ptr object_class_type, a_source_position *position) /* Add destruction information, if needed, to the dynamic initialization entry dip. dip is initializing an object of type class_type. If that is being done as part of a larger complete object, object_class_type indicates that type; otherwise, it is the same as class_type, or it is NULL if not needed. position is a source position to be used for errors. Note that this routine fills in the "destructor" field but does not add the dynamic initialization entry to an object lifetime list (for that, see set_temp_init_dynamic_init_lifetime, among others). */ { a_routine_ptr dtor; check_assertion(is_class_struct_union_type(class_type)); /* Note how this routine interacts with fix_up_dynamic_init_dtors. */ if (expr_stack == NULL || !expr_stack->in_cctor_elision_initializer) { dtor = expr_select_destructor(class_type, object_class_type, position, /*honor_virtual=*/FALSE); record_dtor_in_dynamic_init(dtor, dip, expr_stack == NULL || expr_stack->potentially_evaluated); } else { /* In a cctor elision expression. Put the destructor in the entry, but do not do the access checking etc. at this time. Build a fixup entry to remind us to do the check later, and put it on the list for the current expression. */ a_class_symbol_supplement_ptr cssp = symbol_supplement_for_class(class_type); /* We shouldn't get a different complete object type in an elision context; that happens only in constructor mem-initializers right now. */ check_assertion(object_class_type == NULL || same_entities(class_type, object_class_type)); if (cssp != NULL && has_nontrivial_destructor(cssp)) { a_symbol_ptr dtor_sym = cssp->destructor; dtor = dtor_sym->variant.routine.ptr; record_dtor_in_dynamic_init(dtor, dip, expr_stack->potentially_evaluated); (void)alloc_dynamic_init_dtor_fixup(dip, position); } /* if */ } /* if */ } /* add_dtor_to_dynamic_init */ a_dynamic_init_ptr alloc_dtor_dynamic_init(a_dynamic_init_kind kind, a_type_ptr type, a_source_position *position) /* Allocate a dynamic initialization entry, clear it to default values, set its kind to kind, and return a pointer to it. If type is a type that requires a destructor, put the destructor routine pointer into the dynamic initialization entry. *position gives the associated source position. */ { a_dynamic_init_ptr dip = alloc_expr_dynamic_init(kind); if (C_dialect == C_dialect_cplusplus && is_class_struct_union_type(type)) { /* The type is a class. If it has a destructor, indicate it in the dynamic initialization. */ add_dtor_to_dynamic_init(dip, type, type, position); } /* if */ return dip; } /* alloc_dtor_dynamic_init */ void set_temp_dynamic_init_lifetime(a_dynamic_init_ptr dip) /* The indicated dynamic initialization entry initializes a temporary. If it indicates a destructor, add it to the current object lifetime. */ { if (curr_expr_is_potentially_evaluated()) { /* Put the destruction (if any) on the list for the current object lifetime. But don't do that if the dynamic init is potentially evaluated but not evaluated. For potentially-evaluated expressions, the code is in the tree and we need to make the tree consistent on the assumption that a back end will generate the dead code, but we don't need to put the entry on the destructions list or generate the cleanup code. */ a_boolean block_lifetime = expr_stack->expansion_initializer || (expr_stack->range_based_for_range && extended_range_based_for_lifetime); if (curr_expr_kind_is_const()) { /* In constant-expression contexts, we do not have to generate code for the destruction either. (Pre-C++11 that is an error if the destruction is non-trivial and evaluated. In C++11 a destruction in a sub-expression is permitted if that sub-expression is not actually evaluated.) */ #if CHECKING if ((dip->destructor != NULL || is_dynamic_init_for_vla(dip)) && !constexpr_enabled && expr_stack->evaluated) { expect_error(); } /* if */ #endif /* CHECKING */ } else if (curr_expr_is_evaluated()) { record_end_of_lifetime_destruction(dip, /*static_lifetime=*/FALSE, block_lifetime); } /* if */ if (!block_lifetime) { dip->has_temporary_lifetime = TRUE; if (dyn_init_is(dip, dik_constant) && constexpr_enabled) { a_dynamic_init_ptr udip = skip_constexpr_init_folding(dip); udip->has_temporary_lifetime = TRUE; } /* if */ } /* if */ } /* if */ } /* set_temp_dynamic_init_lifetime */ void set_temp_init_dynamic_init_lifetime(an_expr_node_ptr temp_init_node) /* If the dynamic initialization attached to the indicated enk_temp_init requires a later destruction, put it into the current object lifetime. */ { if (expr_stack != NULL && curr_expr_is_potentially_evaluated()) { a_dynamic_init_ptr dip = temp_init_node->variant.init.dynamic_init; set_temp_dynamic_init_lifetime(dip); /* If the lifetime happens to turn out to be static (e.g., when long lifetime temps are enabled), mark the temp init as requiring a static temporary. */ if (dip->lifetime != NULL && dip->lifetime->kind == (an_object_lifetime_kind)olk_global_static) { dip->static_temp = TRUE; } /* if */ } /* if */ } /* set_temp_init_dynamic_init_lifetime */ an_expr_node_ptr alloc_temp_init_node(a_type_ptr temp_type, a_dynamic_init_ptr dip, a_boolean is_lvalue, a_boolean is_explicit_cast) /* Create an enk_temp_init node and return a pointer to it. The implied temporary has type temp_type. The initialization to be done is pointed to by dip. The enk_temp_init is an lvalue for the temporary (rather than an value) if is_lvalue is TRUE. is_explicit_cast is TRUE if this node represents an explicit cast. */ { an_expr_node_ptr temp_init_node; a_scope_stack_entry_ptr ssep = &scope_stack[decl_scope_level]; temp_init_node = alloc_expr_node((an_expr_node_kind)enk_temp_init); temp_init_node->variant.init.dynamic_init = dip; temp_init_node->is_lvalue = is_lvalue; if (is_lvalue) { temp_init_node->type = temp_type; } else { /* The result is the value of the temporary, so the type is the type of the temporary as a prvalue. */ temp_init_node->type = prvalue_type(temp_type); } /* if */ if (is_explicit_cast) { dip->is_explicit_cast = TRUE; if (constexpr_enabled) { /* dip might be the result of constexpr evaluation of a constructor call, in which case an underlying dik_constructor entry should also be marked as representing an explicit cast. */ a_dynamic_init_ptr udip = skip_constexpr_init_folding(dip); if (udip != dip) { udip->is_explicit_cast = TRUE; } /* if */ } /* if */ } /* if */ /* Make sure the IL scope that the temporary is part of exists. Even though the temporary does not exist as a variable, it's still (from a language point of view) part of this scope. That's important, because it has to be destroyed at the right point. (Note, however, that when a temp is created for a default argument in the context of a function prototype scope, or a template declaration scope, no IL scope will be created; that's okay, since the expression will be copied in a context that will have an IL scope.) */ /* Class reactivation scopes can show up here with severe errors. Skip to the logically enclosing context if that's the case. */ while (scope_is(ssep, sck_class_reactivation)) { expect_error(); ssep = &scope_stack[ssep->previous_scope]; } /* if */ if (!scope_is(ssep, sck_func_prototype) && !scope_is(ssep, sck_template_declaration) && !scope_is(ssep, sck_template_instantiation) && !scope_is(ssep, sck_module_isolated)) { (void)ensure_il_scope_exists(ssep); } /* if */ /* Put the dynamic initialization on a destruction list if appropriate. */ set_temp_init_dynamic_init_lifetime(temp_init_node); if (nodiscard_attribute_enabled && expr_stack != NULL && type_has_nodiscard_attribute(temp_type, NULL)) { /* Note this for processing later. */ expr_stack->nodiscard_expr_seen = TRUE; } /* if */ return temp_init_node; } /* alloc_temp_init_node */ a_boolean error_on_abstract_class_object(a_type_ptr object_type, a_source_position *position) /* An object of the type object_type is being created. If it has an abstract class type, issue an error and return TRUE. Otherwise, return FALSE. */ { a_boolean err = FALSE; if (is_abstract_class_type(object_type)) { err = TRUE; if (expr_error_should_be_issued()) { abstract_class_diagnostic(es_error, ec_abstract_class_object_not_allowed, object_type, position); } /* if */ } /* if */ return err; } /* error_on_abstract_class_object */ an_expr_node_ptr create_expr_temporary( a_type_ptr temp_type, a_boolean is_lvalue, a_boolean is_explicit_cast, a_boolean suppress_abstract_test, a_dynamic_init_kind init_kind, a_source_position *position, a_dynamic_init_ptr *dip) /* Create an enk_temp_init node and return a pointer to it. The implied temporary has type temp_type. A dynamic initialization entry indicating init_kind initialization (and destruction if appropriate) is attached under the enk_temp_init node, and a pointer to it is returned in *dip. The value of the enk_temp_init is an lvalue (rather than an rvalue) for the temporary if is_lvalue is TRUE. is_explicit_cast is TRUE if this node represents an explicit cast. An error is issued if the temporary has an abstract class type unless suppress_abstract_test is TRUE. *position is the position of the reference. Used only in C++. When init_kind == dik_constructor, does not expand constexpr calls (it doesn't have the argument list and other info to do so). */ { an_expr_node_ptr temp_init_node; /* Allocate the dynamic initialization entry. */ *dip = alloc_dtor_dynamic_init(init_kind, temp_type, position); if (curr_expr_kind_is_const() && (*dip)->destructor != NULL && !constexpr_enabled && curr_expr_is_evaluated()) { /* An operation with a destruction is not allowed in an evaluated subexpression of a constant expression and we would get into trouble trying to attach the dynamic init to a lifetime. When constexpr is enabled, an operation with a destruction can appear as a sub-expression provided that sub-expression is never evaluated (the constexpr interpreter will catch such cases). */ expr_pos_error(ec_expr_not_constant, position); (*dip)->destructor = NULL; } /* if */ /* Make an enk_temp_init node that points at the dynamic init entry. */ temp_init_node = alloc_temp_init_node(temp_type, *dip, is_lvalue, is_explicit_cast); if (!suppress_abstract_test && !microsoft_bugs) { /* It's an error to create a temporary of an abstract class type. */ (void)error_on_abstract_class_object(temp_type, position); } /* if */ return temp_init_node; } /* create_expr_temporary */ a_routine_ptr routine_from_function_operand(an_operand *operand) /* operand is the operand identifying the function to call in a normal call (including a virtual call), or some other operand identifying a function. If it is possible to determine the specific function named, return a pointer to its routine entry. Otherwise, return NULL. The operand can be an lvalue (function designator) or prvalue pointer to the function, even though calls actually always use the prvalue form. */ { a_routine_ptr routine = NULL; if (is_expression_operand(operand)) { routine = routine_from_function_expr(operand->variant.expression); } else if (is_constant_operand(operand) && is_a_prvalue(operand)) { a_constant_ptr con = &operand->variant.constant; if (con_is_exact_addr_of_routine(con)) { /* Constant that is the address of a routine. */ routine = con->variant.address.variant.routine; } /* if */ } /* if */ if (routine != NULL && routine->has_deducible_return_type && !routine->has_deduced_return_type) { finalize_deduced_return_type(routine, &operand->position); } /* if */ return routine; } /* routine_from_function_operand */ #if OPTIMIZE_VIRTUAL_FUNCTION_CALLS an_expr_node_ptr retrace_base_casts(an_expr_node_ptr base_cast_node, a_type_ptr target_class, a_type_ptr qualifiers_model, an_expr_node_ptr *new_top_of_tree) /* Recursively traverse a tree of base class cast expression nodes, creating and linking a tree of derived class cast expression nodes that is its inverse. That is, given an expression tree representing the casts (T1*)(T2*)...(TN*) where T[n] is a base class of T[n+1], this routine recursively traverses the expression nodes starting with the cast to T1* and produces the tree corresponding to (TN*)...(T2*) base_cast_node is the current node in the traversal; it must be either an eok_base_class_cast node or one or more eok_cast nodes whose operand and target types are the same except for qualification, followed by an eok_base_class_cast node. target_class is the type at which the scan is to terminate ("TN"). qualifiers_model is a class type whose cv-qualifiers should be used at each cast step (they are the cv-qualifiers we want to end up with on the final result). new_top_of_tree points to an expression node pointer that will be set to point to the new eok_derived_class_cast node that produces the ultimate type (the cast to "TN*"). If the tree of derived class cast nodes is successfully created, the return value is the newly-created derived class cast node for this level; it will have the compiler_generated flag set to TRUE, and will have no operand (it is the caller's responsibility to set the operand to point to the caller's node or to the original top of the tree). If the original tree of cast nodes does not satisfy the requirements described above, no derived class cast nodes will be created and the return value will be NULL. On successful return, *new_top_of_tree points to the top of a sequence of base class casts, and the function result points to the bottom of that sequence, which is a cast that has no operand yet. The top-most caller will attach that bottom node to the original tree, forming an expression (at *new_top_of_tree) that casts the original expression back to the target_class. In the recursive calls, this routine adds another cast to the bottom of the list being built up, and returns a pointer to the bottom-most entry. The casts can also be done on class objects (lvalue or rvalue) in addition to the pointer-to-class case described above. */ { an_expr_node_ptr new_derived_cast_node = NULL; a_type_ptr this_node_class; a_type_ptr operand_class; a_boolean pointer_case = is_pointer_type(base_cast_node->type); base_cast_node = skip_parens(base_cast_node); if (pointer_case) { /* The object is a pointer to class. Skip any casts that adjust the cv-qualifiers. */ while (is_operation_node(base_cast_node) && node_operator_is(base_cast_node, eok_cast) && is_pointer_type(base_cast_node->type) && is_pointer_type(base_cast_node->variant.operation.operands->type)) { a_type_ptr target_type = type_pointed_to(base_cast_node->type); a_type_ptr source_type = type_pointed_to(base_cast_node->variant.operation.operands->type); target_type = skip_typerefs(target_type); source_type = skip_typerefs(source_type); if (identical_types(target_type, source_type)) { base_cast_node=skip_parens(base_cast_node->variant.operation.operands); } else { /* Not a skippable eok_cast node. */ break; } /* if */ } /* while */ } else { /* The object is a class lvalue or rvalue. Skip any nodes that adjust the cv-qualifiers. */ while (is_operation_node(base_cast_node) && (node_operator_is(base_cast_node, eok_lvalue_adjust) || node_operator_is(base_cast_node, eok_class_rvalue_adjust)) && identical_types_ignoring_qualifiers(base_cast_node->type, base_cast_node->variant. operation.operands->type)) { base_cast_node = skip_parens(base_cast_node->variant.operation.operands); } /* while */ } /* if */ if (is_operation_node(base_cast_node) && node_operator_is(base_cast_node, eok_base_class_cast)) { /* This is an eok_base_class_cast node, so processing can continue. Any other kind of node renders the tree impossible to invert and will result in terminating the traversal and returning NULL instead of a new derived class cast node. */ a_base_class_ptr base_class; a_type_ptr cast_dest_type; this_node_class = base_cast_node->type; if (pointer_case) this_node_class = type_pointed_to(this_node_class); this_node_class = skip_typerefs(this_node_class); operand_class = base_cast_node->variant.operation.operands->type; if (pointer_case) operand_class = type_pointed_to(operand_class); operand_class = skip_typerefs(operand_class); cast_dest_type = type_plus_qualifiers_from_second_type(operand_class, qualifiers_model); if (pointer_case) cast_dest_type = make_pointer_type(cast_dest_type); check_assertion(is_immediate_class_type(this_node_class) && is_immediate_class_type(operand_class)); base_class = find_base_class_of(operand_class, this_node_class); if (!base_class->is_virtual) { /* Not virtual. (We cannot cast to a virtual derived class, so failing this test will terminate the traversal and return NULL instead of a new derived class cast node.) */ /* Create the bottom-most node in the chain of derived-class casts. If we've reached the end of the traversal, this is also the top-most node. */ new_derived_cast_node = make_operator_node((an_expr_operator_kind)eok_derived_class_cast, cast_dest_type, (an_expr_node_ptr)NULL); copy_node_value_category(base_cast_node, new_derived_cast_node); new_derived_cast_node->compiler_generated = TRUE; if (identical_types(operand_class, target_class)) { /* We've reached the end of the traversal successfully. The node we just created is also the top of the tree. */ *new_top_of_tree = new_derived_cast_node; } else { /* We're not done with the traversal. Call this routine recursively to complete it */ an_expr_node_ptr new_parent = retrace_base_casts(base_cast_node->variant.operation.operands, target_class, qualifiers_model, new_top_of_tree); if (new_parent != NULL) { /* The rest of the traversal succeeded, so link the node created above to the bottom of the tree of casts. */ new_parent->variant.operation.operands = new_derived_cast_node; } else { /* Discard the node we created. */ new_derived_cast_node = NULL; } /* if */ } /* if */ } /* if */ } /* if */ return new_derived_cast_node; } /* retrace_base_casts */ #endif /* OPTIMIZE_VIRTUAL_FUNCTION_CALLS */ a_routine_ptr final_overrider(a_routine_ptr base_class_function, an_expr_node_ptr implicit_this_arg, a_type_ptr complete_object_type) /* Return the final overrider of base_class_function in the class complete_object_type. base_class_function is a virtual function, and complete_object_type is either the class of base_class_function or a class derived from it. implicit_this_arg is an expression containing eok_base_class_cast nodes leading from complete_object_type to the class of base_class_function. If the derivation is ambiguous, this chain of casts is used to disambiguate the derivation path. */ { a_routine_ptr target_function = base_class_function; a_type_ptr class_of_function = parent_class_of(base_class_function); if (!identical_types(complete_object_type, class_of_function)) { /* The complete object type is a derived class, so there might be an overriding function in it or an intermediate base class. */ an_overriding_virtual_function_ptr virt_func; a_base_class_ptr base_class = find_base_class_of(complete_object_type, class_of_function); check_assertion(base_class != NULL && !base_class->is_pack_expansion); if (base_class->ambiguous) { /* There is more than one derivation path from the class of the function to the complete object type. There might be different overriders on the various paths, some pure and some not, so we can't rely on the override information in base_class (which only reflects one of the paths). We must traverse backward through the implicit_this_arg base-class casts to find the correct final overrider. */ an_expr_node_ptr this_node; a_type_ptr this_base = class_of_function; a_boolean pointer_case = is_pointer_type(implicit_this_arg->type); for (this_node = implicit_this_arg; is_operation_node(this_node) && !same_entities(this_base, complete_object_type); this_node = this_node->variant.operation.operands) { if (this_node->variant.operation.kind == (an_expr_operator_kind)eok_base_class_cast) { /* The expression tree might have nodes other than eok_base_class_cast operations (e.g., cv-qualification casts, parentheses), but we can ignore those. */ a_type_ptr this_derived =this_node->variant.operation.operands->type; if (pointer_case) this_derived = type_pointed_to(this_derived); this_derived = skip_typerefs(this_derived); base_class = find_base_class_of(this_derived, class_of_function); check_assertion(!base_class->is_pack_expansion); for (virt_func = base_class->variant.overriding_virtual_functions; virt_func != NULL; virt_func = virt_func->next) { if (same_entities(virt_func->primary_function, target_function)) { /* Found an override. */ target_function = virt_func->overriding_function; class_of_function = this_derived; break; } /* if */ } /* for */ this_base = this_derived; } /* if */ } /* for */ } else { /* The derivation is unambiguous and we can rely on the list of virtual functions in base_class. */ for (virt_func = base_class->variant.overriding_virtual_functions; virt_func != NULL; virt_func = virt_func->next) { if (same_entities(virt_func->primary_function, base_class_function)) { /* Found an override. */ target_function = virt_func->overriding_function; break; } /* if */ } /* for */ } /* if */ } /* if */ return target_function; } /* final_overrider */ static a_boolean call_invokes_pure_virtual(a_routine_ptr member_func, an_expr_node_ptr function_node) /* Return TRUE iff: 1) we are currently in the constructor or destructor of the class of member_func or one derived from it, and 2) the object expression of the call is either "this" or "this" modified only by casts ("((B*)this)->f()"), and 3) member_func (or its final overrider in the class of the constructor/destructor) is a pure virtual. (Exception: MSVC++ 6.0 allowed a cast to an abstract class, which might result in a detectable call to a pure virtual function in other contexts, as well. We check for this case, too, in microsoft_bugs mode when microsoft_version < 1300.) member_func points to a virtual function, and function_node points to a function expression to which the argument list (including the implicit "this" argument) has already been appended. */ { a_boolean bad_pure_virt_call = FALSE; an_expr_node_ptr implicit_this_arg = function_node->next; if (innermost_function_scope != NULL) { /* The call is in the context of a function body (as opposed to an initialization in namespace scope). */ a_routine_ptr rp = current_routine_entry(); if (rp->special_kind == (a_special_function_kind)sfk_constructor || rp->special_kind == (a_special_function_kind)sfk_destructor) { /* We are in a constructor or destructor, but we don't know yet whether it's in a class of which member_func is a member. */ a_type_ptr class_of_mf; a_type_ptr class_of_ctor_dtor; class_of_mf = parent_class_of(member_func); class_of_ctor_dtor = parent_class_of(rp); if (is_same_class_or_base_class_thereof(class_of_ctor_dtor, class_of_mf)) { /* The class of the constructor/destructor has member_func as a member. Now check if the object expression in the call is "this" or "this" cast to something ("(base*)this", "(const C*)this", etc.) -- it's not an error to invoke a pure virtual from a constructor/destructor if the object isn't the one under construction/destruction. The argument list, including the implicit "this" argument, has already been appended to the function_node, with the implicit "this" as the first successor. */ an_expr_node_ptr node; /* We scan down through any eok_cast, eok_base_class_cast, or eok_derived_class_cast operators so we can compare against the constructor/destructor's "this" parameter. (Note: the eok_cast case is primarily to handle qualification conversions, like ((const C*)this)->f(). Casts through unrelated types, such as ((C*)(unrelated*)this)->f(), are also acceptable, because they don't affect whether the ultimate result would be an attempt to invoke a pure virtual function. eok_derived_class_casts are added by bind_member_function_operand_to_selector to adjust the type of the implicit "this" argument to an overriding virtual function.) */ /* This scan is similar to the "call_case" processing of expr_complete_object_type, except that it only applies to constructors and destructors and it accepts a wider variety of expression nodes -- we just have to establish the use of the ctor/dtor "this" parameter and identify the final overrider, so we don't need to restrict the implicit "this" argument expression to one that lets us actually call the final overrider. */ for (node = skip_parens(implicit_this_arg); is_operation_node(node) && (node_operator_is(node, eok_cast) || node_operator_is(node, eok_base_class_cast) || node_operator_is(node, eok_derived_class_cast)); node = skip_parens(node->variant.operation.operands)) {} if (is_variable_node(node) && node_variable(node) == innermost_function_scope ->variant.routine.this_param_variable) { /* The call's object expression uses the constructor/destructor's "this" pointer. The final check is whether the final overrider of the function being called is a pure virtual function. */ a_routine_ptr overrider = final_overrider(member_func, implicit_this_arg, class_of_ctor_dtor); bad_pure_virt_call = overrider->pure_virtual; } /* if */ } /* if */ } /* if */ } /* if */ if (!bad_pure_virt_call && microsoft_bugs && microsoft_version < 1300) { /* MSVC++ 6.0 allowed a cast to an abstract class type, so we need to check for that case, too. */ a_type_ptr object_type = NULL; check_assertion(implicit_this_arg != NULL); /* For Coverity. */ if (is_pointer_type(implicit_this_arg->type)) { object_type = pointer_expr_complete_object_type(implicit_this_arg, /*call_case=*/TRUE); } else { object_type = expr_complete_object_type(implicit_this_arg, /*call_case=*/TRUE); } /* if */ if (object_type != NULL && is_class_struct_union_type(object_type)) { /* We know the complete object type, so we can find the actual target of this call. */ a_routine_ptr called_func = final_overrider(member_func, implicit_this_arg, skip_typerefs(object_type)); bad_pure_virt_call = called_func->pure_virtual; } /* if */ } /* if */ return bad_pure_virt_call; } /* call_invokes_pure_virtual */ static a_boolean type_operator_construct_termination_next( a_routine_ptr rout, a_type_ptr function_type, a_boolean uses_operator_syntax) /* We are scanning the operand of a decltype construct from source (i.e., not rescanning the operand) and are about to generate a call to rout (with the given type). If the call is the result of operator syntax, uses_operator_syntax is TRUE (and rout will be an operator function). The caller has determined that what has been scanned so far is compatible with this call being the "top" operation of the decltype operand (ignoring comma operations). Look ahead in the token stream to confirm that we are at the end of the decltype construct (which ensures that no other operation will apply on top of the call), and if so return TRUE; otherwise, return FALSE. */ { a_boolean result = TRUE; /* Assume for now. */ a_scanning_token_cache cache; unsigned long n_extra_parens; /* Determine the number of extra parentheses around the decltype operand. Subtract one for the parentheses of the type operator construct itself. */ n_extra_parens = expr_stack->nested_construct_depth-1; if (uses_operator_syntax) { /* Some operators ([] and postfix ++/--) may have a token left in the upcoming token stream. */ a_token_kind expected_token; check_assertion(rout != NULL); switch (rout->variant.opname_kind) { case onk_subscript: expected_token = (a_token_kind)tok_rbracket; /* Since the subscript operation is still being processed, expr_stack->nested_construct_depth still includes the brackets in its count. */ n_extra_parens -= 1; break; case onk_plus_plus: case onk_minus_minus: { a_param_type_ptr ptp = function_type_params(function_type); if (!routine_type_is_nonstatic_member_function(function_type)) { ptp = ptp->next; } /* if */ if (ptp != NULL) { /* An extra parameter is present: Postfix ++ or --. */ if (rout->variant.opname_kind == (an_opname_kind)onk_plus_plus) { expected_token = (a_token_kind)tok_plus_plus; } else { expected_token = (a_token_kind)tok_minus_minus; } /* if */ } else { /* Prefix ++ or --: No additional operator token should be expected. */ expected_token = (a_token_kind)tok_error; } /* if */ } break; default: expected_token = (a_token_kind)tok_error; break; } /* switch */ if (expected_token != (a_token_kind)tok_error) { /* An operator token remains: Skip it (if it is there as expected). */ if (curr_token == expected_token) { cache_curr_token(cache.ptr()); (void)get_token(); } else { expect_error(); result = FALSE; } /* if */ } /* if */ } /* if */ if (result) { /* Skip the extra parentheses. */ while (n_extra_parens--) { if (curr_token != tok_rparen) { result = FALSE; break; } /* if */ cache_curr_token(cache.ptr()); (void)get_token(); } /* while */ /* Check whether we have reached the end of the type operator operand. */ if (curr_token != tok_rparen) { result = FALSE; } /* if */ } /* if */ rescan_cached_tokens(cache.ptr()); return result; } /* type_operator_construct_termination_next */ a_boolean type_has_nodiscard_attribute(a_type_ptr type, a_const_char **reason) /* Returns TRUE if the type has the nodiscard attribute applied to it. If reason is non-NULL, *reason is set to point to a string literal that appeared as an argument to the [[nodiscard]] attribute (or NULL if there is not one). */ { a_boolean result = FALSE; type = skip_typerefs(type); if (is_immediate_class_type(type) && class_type_supp(type)->has_nodiscard_attribute) { result = TRUE; } else if (is_immediate_enum_type(type) && integer_type_supp(type)->has_nodiscard_attribute) { result = TRUE; } /* if */ if (result && reason != NULL) { an_attribute_ptr ap; ap = attribute_string_literal_arg(ak_nodiscard, &type->source_corresp); if (ap != NULL && is_ordinary_string_constant(ap->arguments->variant.constant)) { *reason = ap->arguments->variant.constant->variant.string.value; } /* if */ } /* if */ return result; } /* type_has_nodiscard_attribute */ static a_boolean intrinsic_takes_reflection_range(int32_t cit) /* Return TRUE if the constexpr intrinsic identified by cit is one of the std::meta functions whose last parameter is a reflection_range of info (substitute, can_substitute, reflect_invoke, and the variadic type traits of [meta.reflection.traits]). For these, the front end records a per-range-type iteration plan at parse time that the interpreter replays to read the range's info elements. */ { a_boolean result; switch (cit) { case cit_std_meta_substitute: case cit_std_meta_can_substitute: case cit_std_meta_reflect_invoke: case cit_std_meta_is_constructible_type: case cit_std_meta_is_trivially_constructible_type: case cit_std_meta_is_nothrow_constructible_type: case cit_std_meta_is_invocable_type: case cit_std_meta_is_invocable_r_type: case cit_std_meta_is_nothrow_invocable_type: case cit_std_meta_is_nothrow_invocable_r_type: case cit_std_meta_common_type: case cit_std_meta_common_reference: case cit_std_meta_invoke_result: result = TRUE; break; default: result = FALSE; } /* switch */ return result; } /* intrinsic_takes_reflection_range */ static an_expr_node_ptr func_call_expr( an_expr_node_ptr function_node, a_type_ptr function_type, a_routine_ptr rout, a_boolean is_virtual, a_boolean virtual_suppressed, a_boolean selector_is_object_pointer, a_boolean compiler_generated, a_boolean is_conversion, a_boolean arg_dep_lookup_suppressed, a_boolean qualified_function_name, ARG_UNUSED a_boolean found_through_adl, a_boolean uses_operator_syntax, a_source_position *pos, an_expr_node_ptr *function_call_node) /* Make an expression for a call of the function indicated by function_node, whose type is function_type, and which is to be called virtually if is_virtual is TRUE, or a pointer-to-member-function call if the type of function_node is pointer-to-member-function. rout is the called function if it is known, or NULL otherwise. The arguments of the call are already attached to function_node. A skip_typerefs need not have been done on function_type. function_type can be a template parameter type or class type in a case where the function to be called is not known because the call is dependent. Return a pointer to the call node. *pos gives the call position. When the function return type is invalid (i.e., incomplete) an error node is returned. If virtual_suppressed is TRUE, the function was named in some way that would suppress calling it as virtual (if indeed it is virtual); that's also reflected in is_virtual, but knowing that the user did it explicitly controls whether a diagnostic is put out in some cases. selector_is_object_pointer is TRUE if the call is a nonstatic member function call and the source form was "->" (or "->*" for the pointer-to-member case) rather than "." (or ".*"). compiler_generated is TRUE if this call is compiler-generated (e.g., for an implicit conversion via a conversion function). is_conversion is TRUE for a call generated for an explicit or implicit conversion (e.g., a conversion function call). arg_dep_lookup_suppressed is TRUE if argument-dependent lookup was suppressed on the call because the function name is not followed by a left parenthesis. qualified_function_name is TRUE if argument-dependent lookup was suppressed because the function name is qualified. found_through_adl is TRUE if the call was resolved only through argument-dependent lookup (i.e., ordinary lookup did not yield the called function). uses_operator_syntax is TRUE when a call to an overloaded operator is the result of operator notation ("a+b") rather than an explicit function call. If non-NULL, function_call_node is the address of an expression node pointer that will be set to point to the actual call node itself (which might be below the node returned because of transformations on the return value). It is returned NULL for some error cases. */ { an_expr_operator_kind op; an_expr_node_ptr call_node; a_type_ptr return_type; an_expr_node_ptr temp_init_node = NULL; a_dynamic_init_ptr dip; a_boolean unknown_dependent_function = FALSE; if (function_call_node != NULL) { *function_call_node = NULL; } /* if */ function_type = skip_typerefs(function_type); if (function_type->kind != (a_type_kind)tk_routine) { check_assertion(is_template_dependent_context() && (is_template_param_type(function_type) || is_immediate_class_type(function_type))); unknown_dependent_function = TRUE; } /* if */ if (!unknown_dependent_function) { /* The function return type must be void or object type and not array type. Half of this check is in add_to_derived_type_list. The check here is necessary because it is valid to declare a function returning a class/struct/enum type that is incomplete at the point of declaration of the function so long as it is completed by the time the function is defined or called (if it is). */ a_source_position *diag_pos = expr_stack->suppress_diagnostics ? NULL : pos; a_boolean allow_incomplete_return_type = expr_stack->allow_call_with_incomplete_return_type; a_boolean function_type_error = FALSE; if (allow_incomplete_return_type && !expr_stack->template_deduction_context) { /* In decltype(), where the top-level operation of produces a call (using call syntax or using an overloaded operator), the return type need not be complete. In non-rescanning contexts, we still have to ensure that the operation is "at the top level". */ if (!type_operator_construct_termination_next(rout, function_type, uses_operator_syntax)) { allow_incomplete_return_type = FALSE; } /* if */ } /* if */ if (!check_function_return_type(function_type, diag_pos, /*is_expr_use=*/TRUE, curr_expr_is_evaluated(), allow_incomplete_return_type, rout)) { function_type_error = TRUE; } /* if */ if (relaxed_abstract_checking) { /* With relaxed abstract checking (P0929R2), parameter types are not checked in non-defining function declarations, so they must be checked in call expressions. */ a_param_type_ptr ptp; for (ptp = function_type->variant.routine.extra_info->param_type_list; ptp != NULL; ptp = ptp->next) { if (is_abstract_class_type(ptp->type)) { function_type_error = TRUE; if (expr_error_should_be_issued()) { abstract_class_diagnostic(es_error, ec_abstract_class_param_type, ptp->type, diag_pos); } /* if */ } /* if */ } /* for */ } /* if */ if (function_type_error) { /* There was some error in the return type or parameter types. A diagnostic was issued in non-SFINAE contexts. */ call_node = error_node(); if (expr_stack->suppress_diagnostics) { record_suppressed_error(); } /* if */ goto done; } /* if */ } /* if */ if (rout != NULL) { /* We know which routine is being called. */ if (curr_expr_is_potentially_evaluated()) { /* It is being called. */ rout->called = TRUE; if (rout->is_virtual) { if (virtual_suppressed) { is_virtual = FALSE; } else { if (call_invokes_pure_virtual(rout, function_node)) { /* Call to pure virtual, e.g., from a constructor or destructor. */ expr_pos_warning(ec_call_of_pure_virtual, pos); } /* if */ /* Suppress virtual function dispatch on "final"/"sealed" members. */ if (rout->final) { is_virtual = FALSE; } else { a_type_ptr class_type = parent_class_of(rout); if (is_immediate_class_type(class_type) && class_type->variant.class_struct_union.final) { is_virtual = FALSE; } /* if */ } /* if */ } /* if */ } /* if */ if (rout->has_nodiscard_attribute) { /* Note this for processing later. */ expr_stack->nodiscard_expr_seen = TRUE; } /* if */ } /* if */ if (expr_stack->record_first_nonconstexpr_call && !rout->is_constexpr) { *p_called_nonconstexpr_routine = rout; expr_stack->record_first_nonconstexpr_call = FALSE; } /* if */ } /* if */ /* Determine the operator to use for the call, and the return type. */ if (unknown_dependent_function) { /* Call to unknown dependent function. */ op = (an_expr_operator_kind)eok_call; return_type = type_of_unknown_templ_param_nontype; } else { return_type = il_return_type_of(function_type); if (rout != NULL) { if (rout->has_deduced_return_type) { return_type = type_without_deduced_auto_placeholder(return_type); } else if (rout->is_deleted && rout->has_deducible_return_type) { /* Avoid having the "auto" type (which looks like a template parameter) leak into the expression IL. */ check_assertion_or_expect_error(expr_stack->suppress_diagnostics); return_type = error_type(); } /* if */ } /* if */ if (is_ptr_to_member_type(function_node->type)) { /* Call using a pointer-to-member-function. */ if (selector_is_object_pointer) { op = (an_expr_operator_kind)eok_points_to_pm_call; } else { op = (an_expr_operator_kind)eok_dot_pm_call; } /* if */ } else if (routine_type_is_nonstatic_member_function(function_type)) { /* Call of nonstatic member function. */ if (selector_is_object_pointer) { op = (an_expr_operator_kind)eok_points_to_member_call; } else { op = (an_expr_operator_kind)eok_dot_member_call; } /* if */ } else { /* C mode call, call of non-member function, or call of static member function. */ op = (an_expr_operator_kind)eok_call; } /* if */ if (nodiscard_attribute_enabled && type_has_nodiscard_attribute(return_type, NULL)) { /* Note this for processing later. */ expr_stack->nodiscard_expr_seen = TRUE; } /* if */ } /* if */ /* Make an expression for the function call. */ call_node = make_operator_node(op, return_type, function_node); if (function_call_node != NULL) { *function_call_node = call_node; } /* if */ call_node->position = *pos; call_node->compiler_generated = compiler_generated; call_node->variant.operation.is_virtual_call = is_virtual; call_node->variant.operation.is_conversion_call = is_conversion; call_node->variant.operation.arg_dependent_lookup_suppressed_on_call = arg_dep_lookup_suppressed; call_node->variant.operation.call_with_qualified_function_name = qualified_function_name; #if BACK_END_IS_CP_GEN_BE call_node->variant.operation.only_found_through_arg_dependent_lookup = found_through_adl; #endif /* BACK_END_IS_CP_GEN_BE */ call_node->variant.operation.call_uses_operator_syntax = uses_operator_syntax; if (strict_cpp17_eval_order) { if (uses_operator_syntax && rout != NULL && !special_kind_is(rout, sfk_udl_operator)) { a_boolean eval_left_to_right, eval_right_to_left; check_assertion(special_kind_is(rout, sfk_operator)); eval_order_for_op_kind(rout->variant.opname_kind, &eval_left_to_right, &eval_right_to_left); call_node->variant.operation.eval_right_to_left = eval_right_to_left; call_node->variant.operation.eval_left_to_right = eval_left_to_right; } else { /* An ordinary call (not operator syntax): The operands have to be evaluated in left-to-right order. */ call_node->variant.operation.eval_left_to_right = TRUE; } /* if */ } /* if */ /* If the return value must be constructed, create the necessary temporary to hold the result. For unknown dependent functions we assume this is not needed, and for certain decltype operands (identified with the flag allow_call_with_incomplete_return_type) the language specifies that no temporary object is created. */ if (!unknown_dependent_function && !expr_stack->allow_call_with_incomplete_return_type && function_type->variant.routine.extra_info->value_returned_by_cctor) { /* An error was already issued for a function returning an abstract class type, so do not issue another on a call of such a function. */ temp_init_node = create_expr_temporary(return_type, /*is_lvalue=*/FALSE, /*is_explicit_cast=*/FALSE, /*suppress_abstract_test=*/TRUE, (a_dynamic_init_kind) dik_class_result_via_ctor, pos, &dip); if (expr_stack->possible_rescan_context) { /* Make sure an operand is saved to enable potential rescanning. */ an_operand opnd; make_expression_operand(call_node, &opnd); call_node = make_node_from_operand(&opnd); } /* if */ dip->variant.expression = call_node; call_node = temp_init_node; } /* if */ /* For the std::meta intrinsics that take a trailing reflection_range (e.g., substitute/can_substitute and the variadic type traits), build (once per range type) the iteration plan that the constexpr interpreter replays to collect the std::meta::info elements of that argument. Doing this here, where the range type is fully known and non-dependent, lets the analysis be shared across calls and avoids re-deriving its iteration per call. The reflection_range argument is always the last parameter and is a reference (R&&). */ if (!unknown_dependent_function && rout != NULL && rout->is_constexpr_intrinsic && intrinsic_takes_reflection_range(rout->number.constexpr_intrinsic) && !expr_stack->suppress_diagnostics && !is_template_dependent_context()) { a_param_type_ptr ptp = function_type->variant.routine.extra_info->param_type_list; while (ptp != NULL && ptp->next != NULL) { ptp = ptp->next; } /* while */ if (ptp != NULL && is_any_reference_type(ptp->type)) { a_type_ptr range_type = type_pointed_to(ptp->type); if (!is_template_dependent_type(range_type)) { (void)build_reflection_range_plan(range_type); } /* if */ } /* if */ } /* if */ done: return call_node; } /* func_call_expr */ void make_function_call(an_expr_node_ptr function_node, a_type_ptr function_type, a_boolean is_virtual, a_boolean virtual_suppressed, a_boolean selector_is_object_pointer, a_boolean compiler_generated, a_boolean is_conversion, a_boolean arg_dep_lookup_suppressed, a_boolean qualified_function_name, a_boolean found_through_adl, a_boolean uses_operator_syntax, a_source_position *start_position, a_source_position *operator_position, a_source_position *end_position, an_operand *result, a_boolean *p_folded, an_expr_node_ptr *p_function_call_node) /* Make an operand for a call of the function indicated by function_node, whose type is function_type, and which is to be called virtually if is_virtual is TRUE, or a pointer-to-member-function call if the type of function_node is pointer-to-member-function. The arguments of the call are already attached to function_node. A skip_typerefs need not have been done on function_type. function_type can be a template parameter type or class type in a case where the function to be called is not known because the call is dependent. If virtual_suppressed is TRUE, the function was named in some way that would suppress calling it as virtual (if indeed it is virtual); that's also reflected in is_virtual, but knowing that the user did it explicitly controls whether a diagnostic is put out in some cases. selector_is_object_pointer is TRUE if the call is a nonstatic member function call and the source form was "->" (or "->*" for the pointer-to-member case) rather than "." (or ".*"). compiler_generated is TRUE if this is a compiler-generated call (e.g., for an implicit conversion via a conversion function). is_conversion is TRUE for a call generated for an explicit or implicit conversion (e.g., a conversion function call). arg_dep_lookup_suppressed is TRUE if argument-dependent lookup was suppressed on the call because the function name was not followed by a left parenthesis. qualified_function_name is TRUE if argument-dependent lookup was suppressed because the function name was qualified. found_through_adl is TRUE if the call was resolved only through argument-dependent lookup (i.e., ordinary lookup did not yield the called function). uses_operator_syntax is TRUE when a call to an overloaded operator is the result of operator notation ("a+b") rather than an explicit function call. In a function-notation call expression, start_position gives the source position of the leftmost character in the postfix-expression that names the function being called. operator_position gives the source position of the left paren of the call. end_position gives the source position of the right paren of the call. For other syntactic forms resulting in function calls, the positions vary depending on the syntax used. If non-NULL, p_function_call_node is the address of an expression node pointer that will be set to point to the actual call node itself (which might be below the expression in the result because of transformations on the return value). It is returned NULL for some error cases. If p_folded is non-NULL, *p_folded is set to reflect whether the call was folded or not. */ { an_expr_node_ptr call_node, rout_node; a_routine_ptr rout; a_boolean folded = FALSE; rout = routine_and_node_from_function_expr(function_node, &rout_node); #if GNU_EXTENSIONS_ALLOWED if (gnu_mode && rout_node != NULL) { if (has_gnu_routine_supp(rout) && gnu_routine_supp(rout)->inline_partner != NULL && !rout->definition_for_inlining_only) { /* If this is a call to a function that has both an entry "for inlining only" and "for non-inline uses", record the call as being to the "for inlining only" version so the inliner can have a chance at processing the call. */ rout_node->variant.routine.ptr = gnu_routine_supp(rout)->inline_partner; check_assertion(gnu_routine_supp(rout)-> inline_partner->definition_for_inlining_only); } /* if */ } /* if */ #endif /* GNU_EXTENSIONS_ALLOWED */ /* Make the function call expression node. */ an_expr_node_ptr underlying_func_call_node = NULL; call_node = func_call_expr(function_node, function_type, rout, is_virtual, virtual_suppressed, selector_is_object_pointer, compiler_generated, is_conversion, arg_dep_lookup_suppressed, qualified_function_name, found_through_adl, uses_operator_syntax, operator_position, &underlying_func_call_node); /* Set the source position information for the outer call node. */ set_expr_position(call_node, start_position, end_position, operator_position); /* If the underlying function call node was set, update the source position for the inner call node. */ if (underlying_func_call_node != NULL) { set_expr_position(underlying_func_call_node, start_position, end_position, operator_position); } /* if */ /* Pass the underlying function call node to the caller if requested. */ if (p_function_call_node != NULL) { *p_function_call_node = underlying_func_call_node; } /* if */ /* Make an operand for the overall call (etc.). */ make_expression_operand(call_node, result); result->position = *operator_position; /* A function call returning a reference is an lvalue. */ if (is_any_reference_type(result->type)) { a_boolean is_rvalue_ref = is_rvalue_reference_type(result->type); add_reference_indirection(result); if (is_rvalue_ref) { /* A call of a function that returns an rvalue reference is an xvalue. */ conv_rvalue_reference_result_to_xvalue(result); } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED } else if (microsoft_bugs && microsoft_version < 1100 && !C_mode() && is_class_struct_union_type(result->type)) { /* In Microsoft C++ mode, a function that returns a class type is considered to return an lvalue. This was changed in MSVC++ 5.0. */ conv_class_prvalue_operand_to_lvalue(result); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ /* Check if the call can be folded to a constant-expression. In contexts where that would be a requirement, issue a diagnostic if appropriate. */ if (p_function_call_node != NULL) { an_expr_node_ptr function_call_node = *p_function_call_node; if (!is_error_operand(result) && function_call_node != NULL) { a_boolean call_folded_to_constant = FALSE; a_diag_list diag_list; clear_diag_list(&diag_list); if (curr_expr_is_potentially_evaluated() && ( #if BUILTIN_FUNCTIONS_ENABLED (rout != NULL && is_gnu_builtin_function(rout) && !rout_type_supp(skip_typerefs(rout->type))->has_ellipsis && rout_type_supp(skip_typerefs(rout->type))->prototyped) || #endif /* BUILTIN_FUNCTIONS_ENABLED */ (constexpr_enabled && (rout == NULL || #if GNU_EXTENSIONS_ALLOWED rout->implicit_alias || #endif /* GNU_EXTENSIONS_ALLOWED */ ((rout->is_constexpr || rout->is_consteval || (rout->is_virtual && constexpr_virtual_enabled && !virtual_suppressed)) && !(rout->is_consteval && expr_stack->consteval_call_need_not_fold))))) && (!expr_stack->in_noexcept_operand_expression || core_constant_expr_is_noexcept || microsoft_mode) && expr_fold_constexpr_call(function_call_node, &rout, result, &diag_list)) { /* The call is to a constexpr function (or a function otherwise known to the front end) and it has been folded to a constant result. Note that calls are usually not folded in the operand of noexcept operands because a call is potentially-throwing, unless the flag core_constant_expr_is_noexcept is TRUE or we are in Microsoft mode (MSVC appears to treat "constant expressions" as noexcept, but not the more general "core constant expressions"). */ call_folded_to_constant = TRUE; #if BUILTIN_FUNCTIONS_ENABLED } else if (rout != NULL) { a_routine_ptr rp = rout; #if GNU_EXTENSIONS_ALLOWED if (rp->implicit_alias && gcc_mode && (gnu_version < 40500 || expr_stack->in_static_initializer)) { /* This is a user-defined routine that is implicitly assumed equivalent to a built-in function (recorded in rp->gnu_extra_info->aliased_routine). GCC folds calls to such routines early. */ rp = gnu_routine_supp(rp)->aliased_routine; } /* if */ #endif /* GNU_EXTENSIONS_ALLOWED */ if (is_gnu_builtin_function(rp) && is_foldable_gnu_builtin_function(rp, (a_boolean *)NULL)) { /* Some __builtin_xxx functions act as constant-expressions. */ if (!is_prototype_instantiation_or_expansion_context() || !operand_is_instantiation_dependent(result)) { call_folded_to_constant = fold_gnu_call_if_possible( result, function_call_node); } else { make_template_param_expr_constant_operand(result); call_folded_to_constant = TRUE; } /* if */ } /* if */ #endif /* BUILTIN_FUNCTIONS_ENABLED */ } /* if */ if (call_folded_to_constant) { folded = TRUE; } else { /* If needed, diagnose the folding failure or record that a constant-expression is now ruled out. */ a_boolean is_consteval, no_diagnostic; is_consteval = rout != NULL && rout->is_consteval; no_diagnostic = relaxed_constexpr_allowed() && (!is_consteval || is_consteval_diag_deferred()); (void)call_did_not_fold_to_constant(rout, result, no_diagnostic, &diag_list, (a_source_position*)NULL); } /* if */ discard_more_info_list(&diag_list); } /* if */ } /* if */ if (p_folded != NULL) *p_folded = folded; } /* make_function_call */ #if MICROSOFT_EXTENSIONS_ALLOWED static a_boolean is_unboxed_unmodifiable_initonly_field(an_operand *operand) /* Return TRUE if operand directly references an initonly field or initonly static data member as an lvalue outside of a constructor context in which it is modifiable. */ { a_boolean result = FALSE; if (is_expression_operand(operand)) { an_expr_node_ptr expr = operand->variant.expression; if (expr->is_lvalue && (is_variable_node(expr) || (is_operation_node(expr) && (node_operator_is(expr, eok_dot_field) || node_operator_is(expr, eok_points_to_field) || node_operator_is(expr, eok_dot_static) || node_operator_is(expr, eok_points_to_static)))) && is_unmodifiable_initonly_field_operand(operand, (a_boolean *)NULL)) { result = TRUE; } /* if */ } /* if */ return result; } /* is_unboxed_unmodifiable_initonly_field */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ void assemble_function_call(an_operand *function_operand, an_operand *bound_function_selector, an_expr_node_ptr argument_list, a_boolean compiler_generated, a_boolean arg_dep_lookup_suppressed, a_boolean qualified_function_name, a_boolean found_through_adl, a_boolean uses_operator_syntax, a_source_position *start_position, a_source_position *operator_position, a_source_position *end_position, an_operand *result, a_boolean *p_folded, an_expr_node_ptr *function_call_node) /* Assemble a function call from the various pieces. *function_operand identifies the function to be called. It can identify an unknown dependent function if the call is dependent. If a selector object is needed, it is provided by *bound_function_selector. argument_list points to the (explicit) argument list. compiler_generated is TRUE if this is a compiler-generated call (e.g., to an overloaded operator function). The call is not of a conversion function. arg_dep_lookup_suppressed is TRUE if argument-dependent lookup was suppressed on the call because the function name was not followed by a left parenthesis. qualified_function_name is TRUE if argument-dependent lookup was suppressed because the function name was qualified. found_through_adl is TRUE if the function to be called was only found through argument-dependent lookup (not through ordinary lookup). uses_operator_syntax is TRUE when a call to an overloaded operator is the result of operator notation ("a+b") rather than an explicit function call. In a function-notation call expression, start_position gives the source position of the leftmost character in the postfix-expression that names the function being called. operator_position gives the source position of the left paren of the call. end_position gives the source position of the right paren of the call. For other syntactic forms resulting in function calls, the positions vary depending on the syntax used. An operand for the overall call is constructed in *result. If non-NULL, function_call_node is the address of an expression node pointer that will be set to point to the actual call node itself (which might be below the expression in the result because of transformations on the return value). It is returned NULL for some error cases. If p_folded is non-NULL, *p_folded is set to reflect whether the call was folded or not. */ { an_expr_node_ptr function_node; an_expr_node_ptr implicit_this_argument; a_type_ptr function_type, function_node_type; a_boolean selector_is_object_pointer = FALSE; a_boolean unknown_dependent_function = FALSE; if (function_call_node != NULL) *function_call_node = NULL; if (is_error_operand(function_operand)) { /* If the function operand is an error, the arguments cannot be linked to it; just make an error operand for the result. */ make_error_operand(result); } else { /* Make an expression node for the function, link it to the argument list, then build a call node that points to the function/arguments list. */ /* Make the function address node. This might have type pointer-to- member-function in a case like (p->*pmf)(). */ function_node = make_node_from_operand(function_operand); function_node_type = function_node->type; if (is_ptr_to_member_type(function_node_type)) { /* Call using a pointer-to-member-function. */ function_type = pm_member_type(function_node_type); } else if (is_pointer_or_handle_type(function_node_type)) { /* Normal call using a pointer to function. */ function_type = type_pointed_to(function_node_type); } else { check_assertion( is_template_dependent_context() && (is_template_param_type_or_ref_thereto(function_node_type) || is_class_struct_union_type(function_node_type))); /* Function is unknown because call is dependent. */ unknown_dependent_function = TRUE; function_type = type_of_unknown_templ_param_nontype; } /* if */ if (function_operand->bound_function) { /* Bound function. bound_function_selector indicates the object. */ a_type_ptr this_type, selector_type, this_class_type; if (unknown_dependent_function) { this_type = NULL; this_class_type = NULL; } else { this_type = implicit_this_param_type_of(function_type); this_class_type = type_pointed_to(this_type); this_class_type = skip_typerefs(this_class_type); } /* if */ LINT_IS_INITIALIZED(bound_function_selector->type); LINT_IS_INITIALIZED(bound_function_selector->selector_is_object_pointer); selector_type = bound_function_selector->type, selector_is_object_pointer = bound_function_selector->selector_is_object_pointer; if (selector_is_object_pointer && is_any_ptr_or_ref_type(selector_type)) { selector_type = type_pointed_to(selector_type); } /* if */ if (unknown_dependent_function || (is_template_dependent_context() && (is_template_dependent_type(selector_type) || is_template_dependent_type(this_type)) && !(type_is_lambda_closure(this_class_type) && identical_types_ignoring_qualifiers(this_class_type, selector_type)))) { /* In a prototype instantiation, a selector might have a type that's not demonstrably related to the "this" type. Leave it alone. We make an exception for closure types in template-dependent contexts: They are marked as nonreal, but we might want to attempt normal semantic resolution so that an expression "[]{ return 1; }()" can be evaluated at compile time. */ dependent_case:; } else { /* There shouldn't be a base-class adjustment here. If there is, make_this_pointer_operand or cast_pointer_for_field_selection did not do their job. */ if (CHECKING || /*lint !e506 */ is_template_dependent_context()) { a_type_ptr arg_class_type = selector_type; arg_class_type = skip_typerefs(arg_class_type); /* Using types_are_compatible so that an error type is considered compatible with anything. */ if (!types_are_compatible(arg_class_type, this_class_type)) { if (is_template_dependent_context()) { goto dependent_case; } else if (is_template_dependent_type(this_class_type)) { /* This can happen in error cases. */ expect_error(); goto dependent_case; } else { unexpected_condition(); } /* if */ } /* if */ } /* if */ /* Cast if necessary to handle any const etc. adjustment. */ if (selector_is_object_pointer) { /* The selector is a pointer to class. */ cast_operand(this_type, bound_function_selector, /*is_implicit_cast=*/TRUE); } else { /* The selector is a class lvalue or rvalue. */ #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && is_routine_node(function_node)) { a_routine_ptr rout = function_node->variant.routine.ptr; if (rout->source_corresp.is_class_member && is_value_class_type(parent_class_of(rout)) && is_unboxed_unmodifiable_initonly_field( bound_function_selector)) { /* A member function is being called on an initonly field outside of a constructor context in which it is allowed to be modified. Because the function call may modify the object, we must make a copy of the object to ensure that the initonly field is not modified. (Note that we do not need to consider the case when rout is a member of a base class of the value class type; such a call involves casting the bound function selector to the base class, which implies a boxing operation and thus does not require an additional copy here.) */ expr_pos_warning(ec_member_function_call_on_initonly_field, &bound_function_selector->position); temp_init_from_operand(bound_function_selector, /*result_is_lvalue=*/FALSE); } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ adjust_class_object_type(bound_function_selector, type_pointed_to(this_type), (a_base_class_ptr)NULL); } /* if */ } /* if */ /* Pass the selector object as the first argument. It can be a pointer to class, a class lvalue, or a class rvalue. */ if (is_constant_operand(bound_function_selector) && constant_is(&bound_function_selector->variant.constant, ck_aggregate)) { /* If the selector is a class constant value (resulting from constant- evaluation), introduce a temporary since the call could modify the object. */ an_operand *opnd = bound_function_selector; a_dynamic_init_ptr temp_dip; implicit_this_argument = create_expr_temporary(opnd->type, /*is_lvalue=*/FALSE, /*is_explicit_cast=*/FALSE, /*suppress_abstract_test=*/TRUE, (a_dynamic_init_kind)dik_constant, &opnd->position, &temp_dip); set_dynamic_init_constant( temp_dip, alloc_shareable_constant(&opnd->variant.constant)); } else { implicit_this_argument = make_node_from_operand(bound_function_selector); } /* if */ implicit_this_argument->next = argument_list; argument_list = implicit_this_argument; } /* if */ /* Make the call node. */ function_node->next = argument_list; make_function_call(function_node, function_type, (a_boolean)function_operand->virtual_function, (a_boolean)function_operand->is_qualified_name, selector_is_object_pointer, compiler_generated, /*is_conversion=*/FALSE, arg_dep_lookup_suppressed, qualified_function_name, found_through_adl, uses_operator_syntax, start_position, operator_position, end_position, result, p_folded, function_call_node); } /* if */ result->position = *operator_position; } /* assemble_function_call */ a_statement_ptr make_call_assignment_statement( a_routine_ptr rout, a_boolean suppress_virtual, an_expr_node_ptr dest, an_expr_node_ptr source, a_source_position *err_pos) /* Create an expression statement pointing to a call operator that calls the operator= function "rout" to assign the lvalue "source" to the object pointed to by "dest". ("dest" is the "this" value for the call.) rout is called non-virtually if suppress_virtual is TRUE. Return a pointer to the statement. *err_pos is a source position to be used for errors (e.g., the function has an invalid return type). This routine is intended to be called from outside of the expression routines. */ { a_statement_ptr stmt; an_expr_node_ptr node, func_addr_node; an_expr_stack_entry expr_stack_entry; an_expr_stack_entry_ptr saved_expr_stack; /* Even though this is not an expression scan, make sure the expr_stack has something on it. If there is already something on the stack, save it, clear the stack, and restore it later. */ saved_expr_stack = expr_stack; expr_stack = NULL; push_expr_stack((an_expression_kind)ek_normal, &expr_stack_entry, /*force_object_lifetime=*/FALSE, /*suppress_object_lifetime=*/TRUE); check_assertion(expr_stack != NULL); if (source->kind == (an_expr_node_kind)enk_object_lifetime) { /* The source expression already has an object lifetime on top, so use that as the lifetime for the entire expression. This happens when a call is generated to an operator= that takes its parameter by value, for a class that has a destructor. */ an_object_lifetime_ptr lifetime = source->variant.object_lifetime.ptr; unbind_object_lifetime(lifetime); lifetime->parent_lifetime = curr_object_lifetime; curr_object_lifetime = lifetime; expr_stack->lifetime = lifetime; source = source->variant.object_lifetime.expr; } else { /* Add an object lifetime to capture any temp-init needed on the return of the call. If it's not used, it will simply be deleted on the pop_expr_stack below. */ push_object_lifetime(iek_none, (char *)NULL, (an_object_lifetime_kind)olk_expr_temporary); expr_stack->lifetime = curr_object_lifetime; } /* if */ /* Make a node for the function. */ func_addr_node = function_rvalue_expr(rout); /* Link the operands to the function node. */ func_addr_node->next = dest; dest->next = source; /* Make the call node. */ node = func_call_expr(func_addr_node, rout->type, rout, rout->is_virtual && !suppress_virtual, rout->is_virtual && suppress_virtual, /*selector_is_object_pointer=*/TRUE, /*compiler_generated=*/TRUE, /*is_conversion=*/FALSE, /*arg_dep_lookup_suppressed=*/FALSE, /*qualified_function_name=*/FALSE, /*found_through_adl=*/FALSE, /*uses_operator_syntax=*/FALSE, err_pos, /*function_call_node=*/(an_expr_node_ptr *)NULL); if (rout->is_consteval) { a_boolean consteval_context = innermost_function_scope != NULL && current_routine_entry()->is_consteval; if (!consteval_context) { an_operand folded_call; a_diag_list diag_list; clear_diag_list(&diag_list); if (expr_fold_constexpr_call(node, &rout, &folded_call, &diag_list)) { node = make_node_from_operand(&folded_call); } /* if */ discard_more_info_list(&diag_list); } /* if */ } /* if */ if (is_any_reference_type(node->type)) { /* The function returns a reference. */ node = add_ref_indirection_to_node(node); } /* if */ node = wrap_up_full_expression(node); /* Allocate the statement. */ stmt = alloc_expr_statement(node); pop_expr_stack(); expr_stack = saved_expr_stack; return stmt; } /* make_call_assignment_statement */ void using_lvalue(an_operand *operand) /* Called when an lvalue is going to be used: the left side of assignment operators, the operand of increment/decrement operators, the left operand of the "." operator, and when an lvalue is converted to an rvalue. This routine checks that the address indicated by the lvalue is valid; specifically, it checks for actually using the element just past the end of an array. It's okay to take the address of that element, but it's not okay to actually reference it. This routine also ensures that variable template specializations are instantiated if needed. "operand" can also be an xvalue. */ { if (is_expression_operand(operand)) { /* glvalue given by an expression. */ an_expr_node *node = skip_parens(operand->variant.expression); if (is_variable_node(node)) { /* If this is a reference to a variable template specialization, make sure the specialization is instantiated. */ if (curr_expr_is_potentially_evaluated()) { instantiate_template_var_if_applicable(node_variable(node)); } /* if */ } else { /* Check for a subscript just past the end of an array. */ an_error_code err_code; a_boolean just_past_end = FALSE; (void)valid_node_if_subscript(operand->variant.expression, &just_past_end, &err_code); if (just_past_end) { expr_pos_warning(err_code, &operand->position); } /* if */ } /* if */ } /* if */ } /* using_lvalue */ static void examine_expr_for_bit_field_selection( 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 bit field selection. */ { if (!is_integral_or_enum_type(expr->type)) { /* No point in continuing if what we're looking at can't be a bit field selection because it's not integral. For example, on a.b[5].c there's no point in continuing on to the subtree a.b[5] because it's class-typed all the way down from there. */ tblock->suppress_subtree_walk = TRUE; } else if (is_bit_field_extract_node(expr)) { /* This expression is a bit-field selection. */ tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* examine_expr_for_bit_field_selection */ static a_boolean is_bit_field_expr(an_expr_node_ptr node) /* Return TRUE if the expression "node" is a bit field expression. */ { a_boolean is_bit_field = FALSE; /* Only a glvalue can be a "bit field" by the standard's definition. A prvalue bit field selection is just a value of integral type. */ if (is_glvalue_node(node)) { /* Walk the expression's addressing parts to see whether this is a bit field reference either at the top level or deeper down. */ an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = examine_expr_for_bit_field_selection; tblock.follow_addressing_path = TRUE; traverse_expr(node, &tblock); is_bit_field = tblock.result; } /* if */ return is_bit_field; } /* is_bit_field_expr */ a_boolean is_bit_field_operand(an_operand *operand) /* Return TRUE if the operand is a bit field. */ { a_boolean is_bit_field = FALSE; if (is_expression_operand(operand)) { if (is_bit_field_expr(operand->variant.expression)) { is_bit_field = TRUE; } /* if */ } /* if */ return is_bit_field; } /* is_bit_field_operand */ a_boolean is_bit_field_whose_address_can_be_taken(a_field_ptr field) /* Return TRUE if the indicated field (a bit field) is one whose address can be taken (as an extension). */ { a_boolean addr_can_be_taken = FALSE; a_targ_size_t field_size, type_size; a_targ_alignment type_alignment, struct_alignment; an_integer_kind int_kind; /* In strict ANSI mode, don't allow this. */ if (addr_of_bit_field_allowed && !strict_ansi_mode) { /* See if the bit field is an even number of bytes long. */ field_size = field->bit_size; if (field_size > 0 && (field_size % targ_char_bit == 0)) { field_size /= targ_char_bit; /* See if the bit field is at an even byte offset. */ if (field->offset_bit_remainder == 0) { /* Get the overall alignment of the structure of which this field is a member. */ struct_alignment = parent_class_of(field)->alignment; /* Look for an integral type that matches the bit field size. */ for (int_kind = (an_integer_kind)0; (int)int_kind < (int)ik_last; int_kind = (an_integer_kind)((int)int_kind + 1)) { if (is_bit_precise_kind(int_kind)) { continue; } /* if */ /* The signedness must match. */ if (int_kind_is_signed[(int)int_kind] == field->bit_field_is_signed) { /* The size and alignment must match. */ get_integer_size_and_alignment(int_kind, &type_size, &type_alignment); if (type_size == field_size && type_alignment <= struct_alignment && field->offset % type_alignment == 0) { addr_can_be_taken = TRUE; break; } /* if */ } /* if */ } /* for */ } /* if */ } /* if */ } /* if */ return addr_can_be_taken; } /* is_bit_field_whose_address_can_be_taken */ static a_boolean is_bit_field_operand_whose_address_can_be_taken( an_operand *operand) /* *operand is a bit-field selection lvalue whose address is being taken. See if it is okay to take the address of the bit field (as an extension), and if so, return TRUE. */ { a_boolean addr_can_be_taken = FALSE; an_expr_node_ptr node; a_field_ptr field; check_assertion(is_expression_operand(operand)); node = skip_parens(operand->variant.expression); if (is_variable_node(node)) { /* If this is a binding for a bit field, use the bound expression instead. */ a_variable_ptr vp = node_variable(node); if (vp->init_kind == (an_init_kind)initk_binding) { node = vp->initializer.bound_expr; } /* if */ } /* if */ /* Only handle the simplest case, not something like "&(i ? x.a : x.b)". A case like that could be handled, but it's tricky, since the subexpressions could have different types. */ if (is_bit_field_extract_node(node)) { field = node_field(node->variant.operation.operands->next); if (is_bit_field_whose_address_can_be_taken(field)) { /* The bit field is one whose size and alignment are such that its address can be taken. */ addr_can_be_taken = TRUE; expr_pos_warning(ec_address_of_bit_field, &operand->position); } /* if */ } /* if */ return addr_can_be_taken; } /* is_bit_field_operand_whose_address_can_be_taken */ static a_boolean check_for_taking_the_address_of_a_bit_field( an_operand *operand, a_source_position *err_pos) /* The address of operand is being taken, either explicitly or in a reference binding or reference cast. Check to see whether the operand is a bit field, and if so issue an error at *err_pos and convert the operand to an error operand. Return TRUE if an error was issued. */ { a_boolean err = FALSE; if (is_bit_field_operand(operand) && /* As an extension, the address of a bit field can be taken if it has the same size and alignment as one of the integral types. */ !(addr_of_bit_field_allowed && is_bit_field_operand_whose_address_can_be_taken(operand))) { expr_pos_error(ec_address_of_bit_field, err_pos); conv_to_error_operand(operand); err = TRUE; } /* if */ return err; } /* check_for_taking_the_address_of_a_bit_field */ #if MICROSOFT_EXTENSIONS_ALLOWED static a_boolean r_is_this_parameter_expr(an_expr_node_ptr expr) /* Helper routine for is_this_parameter_expr_in_current_scope to handle the recursive walk of the expression tree. Traverse the expression and return TRUE if it is equivalent to "this" or "*this". The innermost function scope is assumed to be a member function, and the expression is assumed to have the same type as "this" or "*this". The captured "this" of a lambda is not considered to match. */ { a_boolean result = FALSE; if (is_variable_node(expr)) { a_variable_ptr this_var; check_assertion(innermost_function_scope != NULL); this_var = innermost_function_scope->variant.routine.this_param_variable; check_assertion(this_var != NULL); result = (node_variable(expr) == this_var); } else if (is_operation_node(expr)) { an_expr_operator_kind op = expr->variant.operation.kind; an_expr_node_ptr operand1 = expr->variant.operation.operands; an_expr_node_ptr operand2 = operand1->next; switch (op) { case eok_parens: /* (x): Okay if x is "this" or "*this". */ result = r_is_this_parameter_expr(operand1); break; case eok_indirect: /* *x: Okay if x is "this". */ result = r_is_this_parameter_expr(operand1); break; case eok_address_of: /* &x: Okay if x is "*this". */ result = r_is_this_parameter_expr(operand1); break; case eok_comma: /* (x, y): Okay if y is "this" or "*this". */ result = r_is_this_parameter_expr(operand2); break; case eok_question: /* (x ? y : z): Okay if both y and z are "this" or "*this". */ result = r_is_this_parameter_expr(operand2) && r_is_this_parameter_expr(operand2->next); break; default: /* An operation was encountered that results in the expression not being equivalent to "this" or "*this". */ result = FALSE; break; } /* switch */ } else if (expr->kind == (an_expr_node_kind)enk_object_lifetime) { /* An object lifetime passes the expression through. */ result = r_is_this_parameter_expr(expr->variant.object_lifetime.expr); } else { /* This expression precludes it from being equivalent to "this" or "*this". */ result = FALSE; } /* if */ return result; } /* r_is_this_parameter_expr */ static a_boolean is_this_parameter_expr_in_current_scope( an_expr_node_ptr expr) /* Return TRUE if the expression is equivalent to "this" or "*this". Return FALSE if the innermost function scope is not a non-static member function. The captured "this" of a lambda is not considered to match. This is used to determine if a field selection operator selects a field in the same class instance on which the non-static member function was called. "expr" will be the first operand of an eok_dot_field or eok_points_to_field operator, and will therefore be of class type, pointer to class type, C++/CLI handle to ref class type, or C++/CLI interior_ptr type. */ { a_boolean is_this_parameter_expr = FALSE; if (innermost_function_scope != NULL && innermost_function_scope->variant.routine.this_param_variable != NULL) { a_variable_ptr this_var = innermost_function_scope-> variant.routine.this_param_variable; a_type_ptr this_class_type = type_pointed_to(this_var->type); a_type_ptr expr_class_type = is_pointer_or_handle_type(expr->type) ? type_pointed_to(expr->type) : expr->type; if (identical_types_ignoring_qualifiers(this_class_type, expr_class_type)) { /* The expression has the same type as "this" or "*this", so traverse the expression to see whether it is in fact equivalent to "this" or "*this". */ is_this_parameter_expr = r_is_this_parameter_expr(expr); } /* if */ } /* if */ return is_this_parameter_expr; } /* is_this_parameter_expr_in_current_scope */ static void examine_expr_for_initonly_field_selection( 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 C++/CLI initonly field selection. */ { an_expr_node_ptr initonly_field_node = NULL; an_expr_node_ptr class_instance_node = NULL; if (!is_glvalue_node(expr)) { /* An initonly field selection is a glvalue. */ tblock->suppress_subtree_walk = TRUE; } else if (is_operation_node(expr)) { if (node_operator_is(expr, eok_indirect) || node_operator_is(expr, eok_ref_indirect) || node_operator_is(expr, eok_subscript) || node_operator_is(expr, eok_cli_subscript)) { /* These operations do not result in an initonly field selection. */ tblock->suppress_subtree_walk = TRUE; } else if (node_operator_is(expr, eok_dot_field) || node_operator_is(expr, eok_points_to_field)) { if (node_field(expr->variant.operation.operands->next)->is_initonly) { /* A reference to a nonstatic initonly field. */ class_instance_node = expr->variant.operation.operands; initonly_field_node = expr->variant.operation.operands->next; } /* if */ if (node_operator_is(expr, eok_points_to_field)) { /* There is no need to inspect the left-hand operand, as it has no bearing on the expression being an initonly field selection. We still must inspect the left-hand operand in the eok_dot_field case to handle cases such as (b ? x : y).z where z is not itself an initonly field selection, but either x or y is. */ tblock->suppress_subtree_walk = TRUE; } /* if */ } else if (node_operator_is(expr, eok_dot_static) || node_operator_is(expr, eok_points_to_static)) { if (is_variable_node(expr->variant.operation.operands->next) && node_variable(expr->variant.operation.operands->next)->is_initonly) { /* A reference to a static initonly field of the form x.y or p->y. */ initonly_field_node = expr->variant.operation.operands->next; } /* if */ /* The left-hand operand has no bearing on this expression being an initonly field selection. */ tblock->suppress_subtree_walk = TRUE; } /* if */ } else if (is_variable_node(expr) && node_variable(expr)->is_initonly) { /* A direct reference to a static initonly field. */ initonly_field_node = expr; } /* if */ if (initonly_field_node != NULL) { a_boolean skip_node = FALSE; if (tblock->skip_valid_lvalue_uses_of_initonly_fields && innermost_function_scope != NULL) { /* Skip any initonly field selections that are valid lvalue uses within the innermost function scope. */ a_routine_ptr routine = current_routine_entry(); if (special_kind_is(routine, sfk_constructor)) { if (is_field_node(initonly_field_node) && class_instance_node != NULL && is_this_parameter_expr_in_current_scope(class_instance_node)) { /* The expression is a nonstatic initonly field selection being evaluated within the context of its parent class' instance constructor. This is a valid lvalue use of the field. */ skip_node = TRUE; } /* if */ } else if (special_kind_is(routine, sfk_static_constructor)) { if (is_variable_node(initonly_field_node) && same_entities( parent_class_of(routine), parent_class_of(node_variable(initonly_field_node)))) { /* The expression is a static initonly field selection being evaluated within the context of its parent class' static constructor. This is a valid lvalue use of the field. */ skip_node = TRUE; } /* if */ } /* if */ } /* if */ if (!skip_node) { /* This expression is a C++/CLI initonly field selection. */ tblock->is_static_initonly_field = is_variable_node(initonly_field_node); tblock->result = TRUE; tblock->terminate = TRUE; } /* if */ } /* if */ } /* examine_expr_for_initonly_field_selection */ static a_boolean is_initonly_field_operand( an_operand *operand, a_boolean skip_valid_lvalue_uses, a_boolean *p_is_static_initonly_field) /* If skip_valid_lvalue_uses is FALSE, return TRUE if the operand is a C++/CLI initonly field reference. If skip_valid_lvalue_uses is TRUE, return TRUE only if the operand is a C++/CLI initonly field reference that should be treated as unmodifiable due to its use occurring outside of the constructor context in which it is allowed to be modified. If p_is_static_initonly_field is non-NULL, set *p_is_static_initonly_field to TRUE if the field found is a static initonly member. */ { a_boolean is_initonly_field = FALSE; check_assertion(cli_or_cx_enabled); if (is_an_lvalue(operand) && is_expression_operand(operand)) { /* Walk the expression's addressing parts to see whether this is a C++/CLI initonly field selection either at the top level or deeper down. */ an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = examine_expr_for_initonly_field_selection; tblock.follow_addressing_path = TRUE; tblock.skip_valid_lvalue_uses_of_initonly_fields = skip_valid_lvalue_uses; traverse_expr(operand->variant.expression, &tblock); is_initonly_field = tblock.result; if (is_initonly_field && p_is_static_initonly_field != NULL) { *p_is_static_initonly_field = tblock.is_static_initonly_field; } /* if */ } /* if */ return is_initonly_field; } /* is_initonly_field_operand */ a_boolean is_any_initonly_field_operand(an_operand *operand) /* Return TRUE if the operand is a C++/CLI initonly field reference. */ { check_assertion(cli_or_cx_enabled); return is_initonly_field_operand(operand, /*skip_valid_lvalue_uses=*/FALSE, /*p_is_static_initonly_field=*/ (a_boolean *)NULL); } /* is_any_initonly_field_operand */ static a_boolean is_unmodifiable_initonly_field_operand( an_operand *operand, a_boolean *p_is_static_initonly_field) /* Return TRUE if the operand is a C++/CLI initonly field reference that should be treated as unmodifiable due to its use occurring outside of the constructor context in which it is allowed to be modified. */ { check_assertion(cli_or_cx_enabled); return is_initonly_field_operand(operand, /*skip_valid_lvalue_uses=*/TRUE, p_is_static_initonly_field); } /* is_unmodifiable_initonly_field_operand */ static a_boolean check_for_address_of_or_reference_to_initonly_field( an_operand *operand, a_source_position *err_pos, a_boolean reference_case) /* The address of operand is being taken, either explicitly or in a reference binding or reference cast. Check to see whether the operand is an initonly field selection, and if so issue an error at *err_pos and convert the operand to an error operand. Return TRUE if an error was issued. */ { a_boolean err = FALSE; check_assertion(cli_or_cx_enabled); if (is_unmodifiable_initonly_field_operand(operand, (a_boolean *)NULL)) { expr_pos_error(reference_case ? ec_ref_bound_to_initonly_field : ec_address_of_initonly_field, err_pos); conv_to_error_operand(operand); err = TRUE; } /* if */ return err; } /* check_for_address_of_or_reference_to_initonly_field */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ a_boolean is_dllimport_variable_glvalue(ARG_UNUSED an_expr_node_ptr expr, ARG_UNUSED a_constant *conaddr) /* Return TRUE if the given expression is an enk_variable glvalue node for a dllimport variable. If so, record the address of the variable in conaddr. */ { a_boolean is_constant_addr = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED expr = skip_parens(expr); if (is_variable_node(expr) && is_glvalue_node(expr)) { a_variable_ptr var = node_variable(expr); if (var->decl_modifiers & DM_DLLIMPORT) { is_constant_addr = TRUE; set_variable_address_constant(var, conaddr, /*set_address_taken_flag=*/TRUE); } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ return is_constant_addr; } /* is_dllimport_variable_glvalue */ static a_boolean is_possible_nonstatic_selection_masquerading_as_static( an_expr_node_ptr expr) /* Return TRUE if the given expression is a static selection that might be representing a nonstatic selection. This happens with template-dependent selections where we don't know for sure what member is selected (and therefore we don't know whether it's a static member). Also allow a reference cast on top of the selection. The cases we specifically want to allow are those that come up as idioms for writing offsetof, so we test for a limited set of cases, and only lvalues. */ { a_boolean result = FALSE; if (is_template_dependent_context()) { expr = skip_parens(expr); /* Ignore a reference cast on top. */ if (is_operation_node(expr) && node_operator_is(expr, eok_ref_cast)) { expr = skip_parens(expr->variant.operation.operands); } /* if */ if (is_dependent_static_selection(expr)) { an_expr_node_ptr op1 = expr->variant.operation.operands; an_expr_node_ptr op2 = op1->next; /* We're looking for a second operand that is an eok_lvalue over a tpck_member constant. The eok_lvalue is there because the static selection is producing an lvalue for the member, and in the use case we care about (the "&" operator) the static selection is an lvalue. */ if (is_constant_node(op1) && is_operation_node(op2) && node_operator_is(op2, eok_lvalue)) { an_expr_node_ptr mnode = op2->variant.operation.operands; if (is_constant_node(mnode)) { a_constant_ptr mcon = node_constant(mnode); if (constant_is(mcon, ck_template_param) && mcon->variant.template_param.kind == (a_template_param_constant_kind)tpck_member) { result = TRUE; } /* if */ } /* if */ } /* if */ } /* if */ } /* if */ return result; } /* is_possible_nonstatic_selection_masquerading_as_static */ void take_address_of_or_reference_to_lvalue( an_operand *operand, a_boolean reference_case, a_boolean rvalue_reference_case, ARG_UNUSED a_boolean use_handle_for_ref_class, a_boolean is_builtin_addressof, a_source_position *operator_position) /* Change operand (an lvalue or a function designator) to a prvalue that is: (a) If reference_case is FALSE, a pointer to the lvalue. This is the function of the "&" operator. If use_handle_for_ref_class is TRUE and the operand is a ref class object, add a "%" operator instead to generate a C++/CLI handle. (b) If reference_case is TRUE, a reference to the lvalue (or xvalue, also allowed in this case). This is the value stored in a reference when it is bound to the glvalue. If rvalue_reference_case is TRUE, the reference being bound is an rvalue reference. In both cases, check that the operand's address can be taken, and set the address_taken flag. When operator_position is non-NULL, there is an explicit "&" operator in the source and *operator_position gives its position. When is_builtin_addressof is TRUE, the __builtin_addressof operator appears in the source (and *operator_position gives its position). */ { a_source_position *err_pos = &operand->position; an_expr_node_ptr expr = NULL; a_boolean is_implicit = (operator_position == NULL); check_assertion(!reference_case || is_implicit); if (operator_position != NULL) err_pos = operator_position; if (is_error_operand(operand)) { /* Leave an error operand mostly alone. Mark the address as being taken to prevent cascading diagnostics. */ normalize_error_operand(operand); change_ref_kinds(operand->ref_entries_list, SRK_ADDRESS_TAKEN); } else { if (is_a_function_designator(operand)) { /* Check whether the designated function has an enable_if attribute condition that is not known to be "true". */ a_type_ptr rtp = skip_typerefs(operand->type); if (rtp->kind == (a_type_kind)tk_routine && rtp->variant.routine.extra_info->has_enable_if_attribute) { require_true_enable_if_condition(rtp, &operand->position); } /* if */ #if CHECKING } else if (is_a_glvalue(operand) || (is_builtin_addressof && is_template_dependent_context())) { /* Okay. */ } else { #if DEBUG db_operand(operand); #endif /* DEBUG */ internal_error("take_address_of_or_reference_to_lvalue: not an lvalue"); #endif /* CHECKING */ } /* if */ /* Check for taking the address of a bit field. */ if (check_for_taking_the_address_of_a_bit_field(operand, err_pos)) { /* Error issued by the subroutine. */ #if MICROSOFT_EXTENSIONS_ALLOWED /* Check for taking the address of or binding a reference to an initonly field. */ } else if (cli_or_cx_enabled && check_for_address_of_or_reference_to_initonly_field( operand, err_pos, reference_case)) { /* Error issued by the subroutine. */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else { a_boolean did_not_fold = TRUE; a_boolean template_constant = FALSE; an_operand orig_operand; orig_operand = *operand; if (expr_stack->favor_constant_result) { /* See whether the lvalue address is a constant address. If so, use the constant as the value of the "&" operator. */ if (is_expression_operand(operand) || operand_is_string_literal(operand)) { a_constant_ptr conaddr = local_constant(); an_expr_node_ptr test_expr; expr = make_node_from_operand(operand); test_expr = skip_parens(expr); /* In some modes, same-type casts have no effects. Such casts may be represented using eok_lvalue_cast nodes: Skip over them for testing purposes. */ while (is_operation_node(test_expr) && node_operator_is(test_expr, eok_lvalue_cast) && identical_types(test_expr->type, test_expr->variant.operation.operands ->type)) { test_expr = test_expr->variant.operation.operands; } /* while */ if (curr_expr_kind_is_const()) { if (is_operation_node(test_expr) && node_operator_is(test_expr, eok_points_to_field) && is_constant_node(test_expr->variant.operation.operands) && node_constant_is(test_expr->variant.operation.operands, ck_template_param)) { /* A template-dependent field selection whose first operand is a dependent address. This comes up in offsetof. Create a tpck_expression constant for "&" applied to the field selection. */ template_constant = TRUE; } else if (is_possible_nonstatic_selection_masquerading_as_static( expr)) { /* This may be a template-dependent nonstatic selection represented as a static selection because we don't know the member for sure. Create a tpck_expression constant for "&" applied to it. */ template_constant = TRUE; } else if (microsoft_mode && is_template_dependent_context() && !scope_stack_top().is_rescan) { /* MSVC accepts many nonconstant forms in templates. Emulate this by representing the expression via a tpck_expression constant. */ template_constant = TRUE; } else if (is_operation_node(test_expr) && (node_operator_is(test_expr, eok_dot_static) || node_operator_is(test_expr, eok_points_to_static))) { /* In constant expressions, test the second operand of a static selection to see if its address is constant, e.g., for something like &x.static_member. The first operand will not have side effects because this is a constant expression. */ test_expr = test_expr->variant.operation.operands->next; /* Note that expr remains set to the original expression so that if we do fold to a constant we will record the original static selection as the associated expression. */ } /* if */ } /* if */ if (constant_glvalue_address(test_expr, conaddr, /*address_escapes=*/TRUE)) { /* A glvalue with a constant address (in C++ constant-expression contexts, that includes Microsoft-mode dllimport variables). */ if (cpp11_sfinae_enabled && constant_is(conaddr, ck_template_param) && tpck_is(conaddr, tpck_address) && !is_implicit) { /* The address of a member is better represented as a tpck_expression constant, so we can have a "&" in the IL tree so it can be rescanned. */ template_constant = TRUE; } else { did_not_fold = FALSE; if (reference_case) { if (is_pointer_type(conaddr->type)) { /* For the reference case, change the address constant type to a reference. */ a_type_ptr new_type = type_pointed_to(conaddr->type); if (rvalue_reference_case) { new_type = make_rvalue_reference_type(new_type); } else { new_type = make_reference_type(new_type); } /* if */ conaddr->type = new_type; if (constant_is(conaddr, ck_template_param) && tpck_is(conaddr, tpck_expression)) { /* Propagate the new type to the underlying expression. */ an_expr_node_ptr node; node = expr_node_from_tpck_expression(conaddr); node->type = new_type; node = strip_implicit_operations(node); if (is_constant_node(node)) { node_constant(node)->type = new_type; } /* if */ } /* if */ } /* if */ } /* if */ make_constant_operand(conaddr, operand); } /* if */ } /* if */ release_local_constant(&conaddr); } else if (is_constant_operand(operand)) { template_constant = constant_is(&operand->variant.constant, ck_template_param); did_not_fold = template_constant; } /* if */ } /* if */ if (did_not_fold && !template_constant && curr_expr_kind_is_traditional_const() && curr_expr_is_evaluated() && !(gcc_mode && is_expression_operand(operand) && is_glvalue_for_auto_object(operand->variant.expression, (a_boolean *)NULL))) { /* The "&" operation must fold to a constant in a constant expression. In GNU C mode, allow taking the address of a local variable because there are some folding operations that can fold those to constants. If the end result is not a constant, an error will be issued. */ expr_pos_error(ec_expr_not_constant, err_pos); conv_to_error_operand(operand); } else { a_boolean need_expr = did_not_fold; if (curr_expr_kind_is_one_in_which_const_exprs_are_recorded()) { need_expr = TRUE; } /* if */ if (need_expr) { if (expr == NULL) { expr = make_node_from_operand(operand); } /* if */ if (reference_case) { /* Create the reference-to operator. */ expr = add_reference_to_to_node(expr); expr->position = operand->position; } else { /* Create the "&" operator. */ #if MICROSOFT_EXTENSIONS_ALLOWED if (use_handle_for_ref_class && is_cli_ref_or_interface_class_type(expr->type)) { /* Use the "%" operator to create a handle as the address. */ expr = make_operator_node((an_expr_operator_kind)eok_handle_to, make_handle_type(expr->type), expr); if (is_implicit) { expr->compiler_generated = TRUE; } else { expr->position = *operator_position; } /* if */ } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here. */ if (is_implicit && !template_constant) { /* An implicit "&" operator. */ expr = add_address_of_to_node(expr); } else { a_type_ptr addr_type = (template_constant ? type_of_unknown_templ_param_nontype : type_of_address_of(expr)); if (is_builtin_addressof) { /* Create IL for __builtin_addressof. */ an_expr_node_ptr new_expr = alloc_expr_node((an_expr_node_kind)enk_builtin_operation); new_expr->variant.builtin_operation.kind = (a_builtin_operation_kind)bok_builtin_addressof; new_expr->variant.builtin_operation.operands = expr; new_expr->type = addr_type; expr = new_expr; } else { /* An explicit "&" operator. */ expr = make_operator_node( (an_expr_operator_kind)eok_address_of, addr_type, expr); } /* if */ if (is_implicit) { expr->compiler_generated = TRUE; } else { expr->position = *operator_position; } /* if */ } /* if */ } /* if */ } /* if */ if (did_not_fold) { make_expression_operand(expr, operand); if (template_constant) { make_template_param_expr_constant_operand(operand); } /* if */ } else if (need_expr) { operand->variant.constant.expr = expr; } /* if */ } /* if */ restore_operand_details_incl_ref(operand, &orig_operand); /* Change the kind in the reference entries to address-taken. */ /* This will check for taking the address of a register variable. */ change_ref_kinds(operand->ref_entries_list, SRK_ADDRESS_TAKEN); } /* if */ } /* if */ operand->is_simple_string_literal = FALSE; operand->is_id_expression = FALSE; operand->id_expression_was_parenthesized = FALSE; } /* take_address_of_or_reference_to_lvalue */ void take_address_of_lvalue(an_operand *operand, a_source_position *operator_position) /* Change operand (an lvalue) to a prvalue that is a pointer to the object. This is the function of the "&" operator. Check that the operand isn't a register variable or a bit field, and set the address_taken flag. Also works on function designators. When operator_position is non-NULL, there is an explicit "&" operator in the source and *operator_position gives its position. */ { take_address_of_or_reference_to_lvalue(operand, /*reference_case=*/FALSE, /*rvalue_reference_case=*/FALSE, /*use_handle_for_ref_class=*/FALSE, /*is_builtin_addressof=*/FALSE, operator_position); } /* take_address_of_lvalue */ void take_reference_to_operand(an_operand *operand, a_boolean rvalue_reference_case) /* Change operand (a glvalue or class prvalue) to a prvalue that is a reference to the object. This is the value that is stored in a reference bound to the object. Check that the operand isn't a register variable or a bit field, and set the address_taken flag. Also works on function designators. rvalue_reference_case is TRUE if the reference being bound is an rvalue reference. */ { if (is_error_operand(operand)) { normalize_error_operand(operand); } else if (is_a_glvalue(operand) || is_a_function_designator(operand)) { take_address_of_or_reference_to_lvalue(operand, /*reference_case=*/TRUE, rvalue_reference_case, /*use_handle_for_ref_class=*/FALSE, /*is_builtin_addressof=*/FALSE, (a_source_position *)NULL); } else { /* Binding a reference to a class prvalue. */ an_expr_node_ptr expr = NULL; an_operand orig_operand; check_assertion(is_an_rvalue(operand) && (is_class_struct_union_type(operand->type) || is_template_param_type(operand->type) || is_error_type(operand->type))); orig_operand = *operand; if (is_constant_operand(operand)) { a_constant_ptr con = &operand->variant.constant; a_constant_ptr addr_con = local_constant(); if (!is_template_param_constant_operand(operand)) { set_temporary_address_constant(alloc_unshared_constant(con), addr_con); } else { if (is_template_param_expression_constant_operand(operand)) { expr = expr_node_from_operand(operand); check_assertion(expr != NULL); } else { expr = alloc_node_for_constant(alloc_unshared_constant(con)); } /* if */ expr = add_reference_to_to_node(expr); make_template_param_expr_constant(expr, addr_con); } /* if */ addr_con->type = make_reference_type(operand->type); make_constant_operand(addr_con, operand); release_local_constant(&addr_con); } else { if (is_expression_operand(operand)) { expr = make_node_from_operand(operand); } else { unexpected_condition(); } /* if */ expr = add_reference_to_to_node(expr); expr->position = operand->position; make_expression_operand(expr, operand); } /* if */ restore_operand_details(operand, &orig_operand); } /* if */ } /* take_reference_to_operand */ void modifying_lvalue(an_operand *operand, a_boolean value_used) /* The entity indicated by operand (an lvalue) is being modified (e.g., assigned to, incremented, ...). If value_used, the value of the lvalue is used before it is modified. */ { #if CHECKING if (!is_an_lvalue(operand) && !is_error_operand(operand)) { internal_error("modifying_lvalue: not an lvalue"); } /* if */ #endif /* CHECKING */ using_lvalue(operand); /* Change the kind in the reference entries to modification or use/modification. */ change_ref_kinds(operand->ref_entries_list, value_used ? (SRK_USE | SRK_MODIFICATION) : SRK_MODIFICATION); } /* modifying_lvalue */ void conv_object_pointer_to_lvalue(an_operand *operand) /* operand is a prvalue pointer. Change it to an lvalue for the object pointed to (or a function designator). */ { if (is_error_operand(operand)) { normalize_error_operand(operand); } else { an_expr_node_ptr expr; an_operand orig_operand; orig_operand = *operand; expr = make_node_from_operand(operand); expr = add_indirection_to_node(expr); make_glvalue_expression_operand(expr, operand); restore_operand_details_incl_ref(operand, &orig_operand); } /* if */ } /* conv_object_pointer_to_lvalue */ static a_boolean okay_as_gcc_lvalue_question(an_expr_node_ptr op2, an_expr_node_ptr op3, a_boolean ignore_casts, a_type_ptr *lvalue_type) /* Return TRUE if a prvalue expression that is a "?" operator with the indicated two expressions as the second and third operands can be converted to an lvalue "?" in gcc (not g++) mode. If ignore_casts is TRUE, ignore any casts on top of the expressions. *lvalue_type is set to the result type for the lvalue operation. */ { a_boolean okay = FALSE; a_type_ptr res_type = NULL, type2, type3; a_boolean op2_possible, op3_possible; /* Note that by the time a "?" operation gets here, it has been validated as a correct prvalue operation, so a certain level of compatibility between the operands has already been established. */ /* Do a test conversion of the expressions to lvalues, to see whether they can be converted to lvalues and to find out what their lvalue types are. The lvalue types may have cv-qualifiers that the prvalue versions don't. */ (void)conv_prvalue_expr_to_lvalue(op2, &op2_possible, /*see_if_possible=*/TRUE, /*gcc_lvalue=*/TRUE, ignore_casts, &type2); (void)conv_prvalue_expr_to_lvalue(op3, &op3_possible, /*see_if_possible=*/TRUE, /*gcc_lvalue=*/TRUE, ignore_casts, &type3); if (!op2_possible || !op3_possible) { /* One or both of the operands cannot be converted to an lvalue, so give up. */ } else if (is_class_struct_union_type(type2)) { /* For struct types, only the exact same type will work. */ okay = identical_types(type2, type3); res_type = type2; } else if (!is_scalar_type(type2) || !is_scalar_type(type3)) { /* Rule out void and function types. */ /* okay = FALSE; -- already set. */ } else if (identical_types(type2, type3)) { /* The types match. */ okay = TRUE; res_type = type2; } else if (get_type_qualifiers(type2) == get_type_qualifiers(type3) && still_an_lvalue(type2, type3)) { /* The types match enough that an lvalue cast can bridge the gap. We also require that the cv-qualifiers match, to avoid problems with cases like int i; const int j; (i ? i : j) = 1; // Modifiable or not? */ okay = TRUE; /* Favor an integral type as the result type. */ if (is_integral_type(type2)) { res_type = type2; } else if (is_integral_type(type3)) { res_type = type3; } else { res_type = type2; } /* if */ } /* if */ if (okay) { *lvalue_type = res_type; } /* if */ return okay; } /* okay_as_gcc_lvalue_question */ static an_expr_node_ptr conv_prvalue_expr_to_lvalue( an_expr_node_ptr node, a_boolean *converted, a_boolean see_if_possible, a_boolean gcc_lvalue, a_boolean ignore_casts, a_type_ptr *p_lvalue_type) /* node is a prvalue expression. If possible, rewrite it as an lvalue for the object, and return a pointer to the lvalue expression along with *converted TRUE. If such a conversion is not possible, set *converted FALSE and return the unmodified original expression. If see_if_possible is TRUE, just see if the rewriting is possible, set *converted accordingly, and return the unmodified expression. If gcc_lvalue is TRUE, we're rewriting the operand in a case where gcc (not g++) allows treating an rvalue as an lvalue. If ignore_casts is TRUE, ignore any casts on top of the expression (throw them away, then turn the expression into an lvalue; this will affect the returned expression pointer even if no transformation is done). If p_lvalue_type is non-NULL, *p_lvalue_type is set to the type of the lvalue; it might differ from the original node type in having extra cv-qualifiers. It is set even when see_if_possible is TRUE (in fact, that's the usual use case). This routine undoes glvalue-to-prvalue conversions (which is always possible) and also tracks class prvalues back to the call that generated them, and rewrites the call to produce an lvalue (which is usually but not always possible). This routine is used for reference casts that allow a source expression that is an prvalue, and for certain extensions, such as the GNU and Microsoft bugs that allow some prvalues to be used as if they were lvalues. It is also used in IL lowering, via conv_prvalue_expr_to_object_pointer. As will be clear from that, this routine can be called from outside of the expression routines; it does not reference things like the expr_stack. However, it can't be called from outside the front end, e.g., in a back end. Note that when this routine is called on a lowered expression, the resulting lvalue will have the same type as the prvalue type (this is necessary to prevent const-qualification from being re-introduced once lowering has eliminated it). */ { a_boolean possible = FALSE; a_type_ptr lvalue_type; if (ignore_casts) { node = remove_cast_operations(node); } /* if */ check_assertion(!is_glvalue_node(node)); check_assertion(in_front_end); lvalue_type = node->type; if (node->orig_lvalue_type != NULL) { /* If the lvalue type was saved on the conversion from lvalue to rvalue, use that type. */ lvalue_type = node->orig_lvalue_type; } /* if */ /* No skip_parens here. Parentheses are handled under the enk_operation case. */ if (is_variable_node(node)) { /* The value of a variable. Change it to an lvalue for the variable. */ a_variable_ptr var = node_variable(node); possible = TRUE; /* Re-fetch the variable type, because cv-qualifiers might have been dropped in converting it to an rvalue. */ lvalue_type = var->type; check_assertion(!is_any_reference_type(lvalue_type)); #if DO_IL_LOWERING if (il_lowering_underway && var->is_parameter && var->variant.assoc_param_type != NULL && var->variant.assoc_param_type->passed_via_copy_constructor && is_pointer_type(lvalue_type) && !is_pointer_type(node->type)) { /* IL lowering rewrites the types of variables that are parameters passed by copy constructor, to add a "pointer-to" to the type. If the variable type has been changed already, but the node we're working on hasn't been lowered yet, use the class type, not the pointer type, for the lvalue type. */ lvalue_type = type_pointed_to(lvalue_type); check_assertion(identical_types_ignoring_qualifiers(lvalue_type, node->type)); } /* if */ #endif /* DO_IL_LOWERING */ } else if (node->kind == (an_expr_node_kind)enk_param_ref) { /* A reference to a parameter. Change it to an lvalue by just changing the is_lvalue flag. But a reference to "this" can't be turned into an lvalue. */ if (node->variant.param_ref.param_num != 0) { possible = TRUE; } /* if */ } else if (node->kind == (an_expr_node_kind)enk_temp_init) { /* A temporary initialization indicating the value of a temporary. Change it to an lvalue for the temporary. */ possible = TRUE; } else if (is_constant_node(node)) { a_constant_ptr con = node_constant(node); if (con->is_result_of_constexpr_call && con->expr != NULL && con->expr->kind == (an_expr_node_kind)enk_temp_init) { /* We sometimes fold rvalue enk_temp_init nodes before knowing that they will be turned back into lvalues. */ possible = TRUE; if (!see_if_possible) { node = con->expr; } /* if */ } /* if */ } else if (node->kind == (an_expr_node_kind)enk_lambda) { /* A lambda. Change it to an lvalue by just changing the is_lvalue flag. */ possible = TRUE; } else if (node->kind == (an_expr_node_kind)enk_typeid) { /* An rvalue for a typeid can be turned back into an lvalue. */ possible = TRUE; lvalue_type = make_qualified_type(lvalue_type, TQ_CONST); } else if (node->kind == (an_expr_node_kind)enk_object_lifetime) { /* We don't expect this transformation to be done once the enk_object_lifetime has been added. */ unexpected_condition(); } else if (is_operation_node(node)) { /* An operator node. */ 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; an_expr_node_ptr op3 = NULL; a_boolean op1_possible, op2_possible, op3_possible; /* Note that skip_parens is not called on the operands at this point, because we still want to be able to unlink/relink the original operands. */ switch (op) { case eok_indirect: case eok_ref_indirect: /* The top operator is an indirection, so we can just change the node to an lvalue. */ possible = TRUE; if (is_ptr_or_ref_type(op1->type)) { lvalue_type = type_pointed_to(op1->type); } /* if */ break; case eok_subscript: /* "[]" operator -- transform to an lvalue. */ possible = TRUE; /* Get the lvalue type from the pointer operand, which can be either one. */ if (is_pointer_type(op1->type)) { lvalue_type = type_pointed_to(op1->type); } else if (is_pointer_type(op2->type)) { lvalue_type = type_pointed_to(op2->type); } /* if */ break; #if MICROSOFT_EXTENSIONS_ALLOWED case eok_cli_subscript: /* C++/CLI subscript operator -- transform to an lvalue. */ possible = TRUE; if (is_handle_type(op1->type)) { a_type_ptr arr_type = type_pointed_to(op1->type); if (is_cli_array_type(arr_type)) { lvalue_type = cli_array_element_type(arr_type); } /* if */ } /* if */ break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case eok_parens: /* Parentheses: just do a recursive call on the operand. */ op1 = conv_prvalue_expr_to_lvalue(op1, &possible, see_if_possible, gcc_lvalue, ignore_casts, &lvalue_type); if (possible && !see_if_possible) { node->variant.operation.operands = op1; } /* if */ break; case eok_question: { a_boolean op2_is_throw = FALSE, op3_is_throw = FALSE; /* "?" operator -- transform each branch independently to an lvalue. */ op3 = op2->next; if (C_mode() && !gcc_lvalue) { /* Can't generate an lvalue "?" in C mode in general. */ } else if (is_void_type(lvalue_type)) { /* Can't generate an lvalue when the result type is void. */ } else if (!gcc_lvalue) { /* See if both branches can be rewritten. Note that except for throw cases we know that the two operands have the same type, because they've been converted to a common type. That doesn't guarantee that they can be converted to lvalues. */ (void)conv_prvalue_expr_to_lvalue(op2, &op2_possible, /*see_if_possible=*/TRUE, /*gcc_lvalue=*/FALSE, ignore_casts, &lvalue_type); if (!op2_possible && skip_parens(op2)->kind == (an_expr_node_kind)enk_throw) { op2_is_throw = TRUE; if (!gpp_mode) op2_possible = TRUE; } /* if */ (void)conv_prvalue_expr_to_lvalue(op3, &op3_possible, /*see_if_possible=*/TRUE, /*gcc_lvalue=*/FALSE, ignore_casts, &lvalue_type); if (!op3_possible && skip_parens(op3)->kind == (an_expr_node_kind)enk_throw) { op3_is_throw = TRUE; if (!gpp_mode) op3_possible = TRUE; } /* if */ possible = (op2_possible && op3_possible); } else { /* Test whether this expression can be rewritten as an lvalue in gcc mode. */ possible = okay_as_gcc_lvalue_question(op2, op3, ignore_casts, &lvalue_type); } /* if */ if (possible) { if (!see_if_possible) { node->variant.operation.returns_lvalue_instead_of_usual_rvalue = TRUE; node->is_lvalue = TRUE; op2->next = NULL; if (!op2_is_throw) { op2 = conv_prvalue_expr_to_lvalue(op2, &op2_possible, /*see_if_possible=*/FALSE, gcc_lvalue, ignore_casts, (a_type_ptr *)NULL); } /* if */ if (!op3_is_throw) { op3 = conv_prvalue_expr_to_lvalue(op3, &op3_possible, /*see_if_possible=*/FALSE, gcc_lvalue, ignore_casts, (a_type_ptr *)NULL); } /* if */ if (gcc_lvalue) { /* For the gcc case, cast the operands to the right result type if necessary. */ if (!identical_types(op2->type, lvalue_type)) { op2 = make_lvalue_cast_node(op2, lvalue_type, /*compiler_generated=*/TRUE); } /* if */ if (!identical_types(op3->type, lvalue_type)) { op3 = make_lvalue_cast_node(op3, lvalue_type, /*compiler_generated=*/TRUE); } /* if */ } /* if */ op1->next = op2; op2->next = op3; } /* if */ } /* if */ } break; case eok_comma: /* "," operator -- try to transform the second operand to an lvalue. */ /* Can't do this in C mode in general. */ if (!C_mode() || gcc_lvalue) { op2 = conv_prvalue_expr_to_lvalue(op2, &possible, see_if_possible, gcc_lvalue, ignore_casts, &lvalue_type); if (possible && !see_if_possible) { node->variant.operation.returns_lvalue_instead_of_usual_rvalue = TRUE; op1->next = op2; } /* if */ } /* if */ break; case eok_dot_static: case eok_points_to_static: /* Static selection -- try to transform the second operand to an lvalue. */ op2 = conv_prvalue_expr_to_lvalue(op2, &possible, see_if_possible, gcc_lvalue, ignore_casts, &lvalue_type); if (possible && !see_if_possible) { op1->next = op2; } /* if */ break; #if GNU_EXTENSIONS_ALLOWED case eok_gnu_min: case eok_gnu_max: /* GNU C++ min/max operators (?) -- transform both operands to lvalues. */ (void)conv_prvalue_expr_to_lvalue(op1, &op1_possible, /*see_if_possible=*/TRUE, /*gcc_lvalue=*/FALSE, ignore_casts, &lvalue_type); (void)conv_prvalue_expr_to_lvalue(op2, &op2_possible, /*see_if_possible=*/TRUE, /*gcc_lvalue=*/FALSE, ignore_casts, &lvalue_type); possible = (op1_possible && op2_possible); if (possible) { if (!see_if_possible) { op1->next = NULL; op1 = conv_prvalue_expr_to_lvalue(op1, &op1_possible, /*see_if_possible=*/FALSE, gcc_lvalue, ignore_casts, (a_type_ptr *)NULL); op2 = conv_prvalue_expr_to_lvalue(op2, &op2_possible, /*see_if_possible=*/FALSE, gcc_lvalue, ignore_casts, (a_type_ptr *)NULL); node->variant.operation.operands = op1; op1->next = op2; } /* if */ } /* if */ break; #endif /* GNU_EXTENSIONS_ALLOWED */ case eok_dot_field: case eok_pm_field: /* Selection of a field from a class. */ /* Also pointer-to-member selection, "x.*pm". */ { a_type_ptr class_type; if (is_glvalue_node(op1)) { /* The selection has a glvalue-to-prvalue conversion built into it, so we can undo that by simply changing the flag. */ possible = TRUE; class_type = op1->type; } else { /* The selection is a selection out of a prvalue, so we try to convert the first operand to an lvalue. */ (void)conv_prvalue_expr_to_lvalue(op1, &op1_possible, /*see_if_possible=*/TRUE, gcc_lvalue, ignore_casts, &class_type); if (op1_possible) { possible = TRUE; if (!see_if_possible) { op1->next = NULL; op1 = conv_prvalue_expr_to_lvalue(op1, &op1_possible, /*see_if_possible=*/FALSE, gcc_lvalue, ignore_casts, (a_type_ptr *)NULL); node->variant.operation.operands = op1; op1->next = op2; } /* if */ } /* if */ } /* if */ if (possible) { if (op == (an_expr_operator_kind)eok_dot_field) { lvalue_type = make_field_selection_type( node_field(op2), get_type_qualifiers(class_type)); } else { check_assertion(op == (an_expr_operator_kind)eok_pm_field); lvalue_type = make_pm_selection_type(op1->type, op2->type); } /* if */ } /* if */ } break; case eok_points_to_field: case eok_pm_points_to_field: /* Selection of a field from a class, "->" form. The selection has a glvalue-to-prvalue conversion built into it, so we can undo that by simply changing the flag. */ /* Also pointer-to-member selection, "p->*pm". */ possible = TRUE; if (is_pointer_type(op1->type)) { a_type_ptr class_type = type_pointed_to(op1->type); if (op == (an_expr_operator_kind)eok_points_to_field) { lvalue_type = make_field_selection_type( node_field(op2), get_type_qualifiers(class_type)); } else { check_assertion(op == (an_expr_operator_kind)eok_pm_field); lvalue_type = make_pm_selection_type(class_type, op2->type); } /* if */ } /* if */ break; case eok_base_class_cast: case eok_derived_class_cast: if (!is_glvalue_node(op1)) { /* Base and derived class casts (class prvalue --> class prvalue). Try to transform the operand to an lvalue. */ op1 = conv_prvalue_expr_to_lvalue(op1, &possible, see_if_possible, gcc_lvalue, /*ignore_casts=*/FALSE, &lvalue_type); } else { possible = TRUE; } /* if */ if (possible) { lvalue_type = type_plus_qualifiers_from_second_type(node->type, lvalue_type); if (!see_if_possible) { node->variant.operation.operands = op1; } /* if */ } /* if */ break; case eok_ref_cast: case eok_lvalue_adjust: case eok_ref_dynamic_cast: /* Lvalue type adjustment or cast to reference type, with a glvalue-to-prvalue conversion built into it. We can undo that by simply changing the flag. */ possible = TRUE; break; case eok_assign: /* Assignment -- we can just turn on the is_lvalue flag. */ goto assignment_case; case eok_class_rvalue_adjust: /* cv-qualifier adjustment on a class prvalue. Try to turn the operand into an lvalue. */ op1 = conv_prvalue_expr_to_lvalue(op1, &possible, see_if_possible, gcc_lvalue, /*ignore_casts=*/FALSE, (a_type_ptr *)NULL); if (possible && !see_if_possible) { /* Change the eok_class_rvalue_adjust to an eok_lvalue_adjust that changes the cv-qualifiers. */ set_node_operator(node, (an_expr_operator_kind)eok_lvalue_adjust, node->type, /*is_lvalue=*/TRUE, op1); } /* if */ break; #if C99_IL_EXTENSIONS_SUPPORTED case eok_imag_part: case eok_real_part: /* GNU __imag and __real. */ if (is_glvalue_node(op1)) { /* The selection has a glvalue-to-prvalue conversion built into it, so we can undo that by simply changing the flag. */ possible = TRUE; lvalue_type = type_plus_qualifiers_from_second_type(node->type, op1->type); } /* if */ break; #endif /* C99_IL_EXTENSIONS_SUPPORTED */ default: if (is_compound_assignment_operator(op)) { /* A compound assignment operator like "+=". We can just turn on the is_lvalue flag to turn the expression into an lvalue. */ assignment_case: possible = TRUE; if (!see_if_possible) { node->variant.operation.returns_lvalue_instead_of_usual_rvalue = TRUE; } /* if */ lvalue_type = op1->type; } /* if */ break; } /* switch */ } else if (is_error_node(node)) { /* An error node stays the same. */ possible = TRUE; } /* if */ /* If the node was transformed, update its type to the lvalue type (that might add cv-qualifiers) and mark the node as an lvalue. */ if (!see_if_possible && possible) { #if DO_IL_LOWERING a_type_ptr orig_type = node->type; #endif /* DO_IL_LOWERING */ node->type = lvalue_type; node->orig_lvalue_type = NULL; node->is_lvalue = TRUE; #if DO_IL_LOWERING if (il_lowering_underway && visited_yet(node) && !identical_types(orig_type, lvalue_type)) { /* If this node has already been lowered (presumably stripping any const qualification), don't re-add it -- simply adjust the type of the resulting lvalue to have the original type. */ node = make_lvalue_operator_node( (an_expr_operator_kind)eok_lvalue_adjust, orig_type, node); } /* if */ #endif /* DO_IL_LOWERING */ } /* if */ *converted = possible; if (p_lvalue_type != NULL && possible) *p_lvalue_type = lvalue_type; return node; } /* conv_prvalue_expr_to_lvalue */ void conv_prvalue_expr_to_object_pointer(an_expr_node_ptr *p_node, a_boolean *converted) /* *p_node is an expression tree for a prvalue. If possible, rewrite it as an object pointer for the object, and set *p_node to the new pointer and *converted to TRUE. If not possible, return *converted FALSE and *p_node unchanged. */ { an_expr_node_ptr node = *p_node; node = conv_prvalue_expr_to_lvalue(node, converted, /*see_if_possible=*/FALSE, /*gcc_lvalue=*/FALSE, /*ignore_casts=*/FALSE, (a_type_ptr *)NULL); if (*converted) { /* Turn an lvalue into a pointer. */ *p_node = add_address_of_to_node(node); } /* if */ } /* conv_prvalue_expr_to_object_pointer */ an_expr_node_ptr strip_rvalue_base_class_casts(an_expr_node_ptr expr, an_expr_node_ptr *top_cast, an_expr_node_ptr *bottom_cast) /* expr is a class rvalue expression. If it has any base class casts on top, remove them and save them on the side via *top_cast and *bottom_cast, and return the expression under the casts as the result of the function. If not, set *top_cast and *bottom_cast to NULL and return the original expression. */ { an_expr_node_ptr node; check_assertion(!expr->is_lvalue && (is_class_struct_union_type(expr->type) || is_template_param_type(expr->type) || is_error_type(expr->type))); *top_cast = *bottom_cast = NULL; node = expr; /* No need to check for eok_parens here because this is a compiler- generated sequence. */ /* Skip one class rvalue type adjustment on top. If there are base class casts underneath, this adjustment will be included in the cast list. */ if (is_operation_node(node) && node_operator_is(node, eok_class_rvalue_adjust)) { node = node->variant.operation.operands; } /* if */ while (is_operation_node(node) && node->compiler_generated && node_operator_is(node, eok_base_class_cast)) { *top_cast = expr; *bottom_cast = node; node = node->variant.operation.operands; } /* while */ if (*top_cast != NULL) { /* We did find a base class cast, so return the expression under the whole sequence of casts to the caller. */ expr = node; } /* if */ return expr; } /* strip_rvalue_base_class_casts */ void conv_class_prvalue_operand_to_glvalue(an_operand *operand, a_boolean is_xvalue) /* Convert a prvalue class operand into an operand for a glvalue for the object. The result is an xvalue if is_xvalue is TRUE, and an lvalue otherwise. If necessary, this will involve creating a temporary and initializing it from the prvalue. This routine is used only in C++ mode. */ { a_boolean fold = FALSE; if (is_error_operand(operand)) { normalize_error_operand(operand); } else { an_operand orig_operand = *operand; an_expr_node_ptr node, top_cast = NULL, bottom_cast = NULL; a_boolean is_expr; /* The operand is a prvalue. In general, we will have to copy the prvalue to a temporary and use the address of the temporary. However, there are some cases that can be optimized. */ check_assertion(is_a_prvalue(operand) && (is_class_struct_union_type(operand->type) || is_template_param_type(operand->type))); if (is_constant_operand(operand) && operand->variant.constant.is_result_of_constexpr_call && operand->variant.constant.expr != NULL && is_temp_node(operand->variant.constant.expr)) { /* Start with the backing expression to avoid adding another constant layer that might hide the backing expression of the underlying constant. We'll attempt to fold it back to a constant below. */ is_expr = TRUE; node = operand->variant.constant.expr; fold = TRUE; } else { is_expr = is_expression_operand(operand); node = make_node_from_operand(operand); } /* if */ if (is_expr) { /* Change the prvalue to an lvalue if possible. */ a_boolean processed = FALSE; node = conv_prvalue_expr_to_lvalue(node, &processed, /*see_if_possible=*/FALSE, /*gcc_lvalue=*/FALSE, /*ignore_casts=*/FALSE, (a_type_ptr *)NULL); if (processed) { /* The expression has been rewritten as an lvalue. */ if (is_xvalue) { node->is_xvalue = TRUE; node->is_lvalue = FALSE; } /* if */ make_glvalue_expression_operand(node, operand); goto done; } /* if */ } /* if */ /* Create a temporary, copy the prvalue into the temporary, and return a glvalue for the temporary. */ if (is_expr) { /* Temporarily remove any class rvalue base class casts so that we make the temporary for the derived class without slicing. The casts will be reattached to the new expression below. */ node = strip_rvalue_base_class_casts(node, &top_cast, &bottom_cast); } /* if */ node = glvalue_from_class_prvalue_node(node, is_xvalue); if (top_cast != NULL) { /* Restore the base class casts on top of the initialization of the temporary. */ bottom_cast->variant.operation.operands = node; node = top_cast; /* Change the rvalue casts to glvalue casts. */ check_assertion(is_operation_node(node)); if (node_operator_is(node, eok_class_rvalue_adjust)) { set_node_operator(node, (an_expr_operator_kind)eok_lvalue_adjust, node->type, /*is_lvalue=*/TRUE, node->variant.operation.operands); if (is_xvalue) { node->is_xvalue = TRUE; node->is_lvalue = FALSE; } /* if */ node = node->variant.operation.operands; } /* if */ for (;;) { check_assertion(is_operation_node(node) && node_operator_is(node, eok_base_class_cast)); if (is_xvalue) { node->is_xvalue = TRUE; } else { node->is_lvalue = TRUE; } /* if */ if (node == bottom_cast) break; node = node->variant.operation.operands; check_assertion(node != NULL); } /* for */ node = top_cast; } /* if */ make_glvalue_expression_operand(node, operand); done: /* Restore the original source position, etc. */ if (fold) { force_operand_to_constant_if_possible(operand); } /* if */ restore_operand_details(operand, &orig_operand); } /* if */ } /* conv_class_prvalue_operand_to_glvalue */ void conv_class_prvalue_operand_to_lvalue(an_operand *operand) /* Convert a prvalue class operand into an operand for an lvalue for the object. If necessary, this will involve creating a temporary and initializing it from the prvalue. This routine is used only in C++ mode. */ { conv_class_prvalue_operand_to_glvalue(operand, /*xvalue=*/FALSE); } /* conv_class_prvalue_operand_to_lvalue */ static an_expr_node_ptr conv_xvalue_expr_to_lvalue(an_expr_node_ptr expr) /* Convert the indicated xvalue expression to an lvalue, and return a pointer to the updated tree. */ { a_boolean change_directly = FALSE; check_assertion(expr->is_xvalue); if (expr->kind == (an_expr_node_kind)enk_temp_init) { /* A temp-init node can be directly changed to be an lvalue. */ change_directly = TRUE; } else if (is_operation_node(expr)) { an_expr_operator_kind op = expr->variant.operation.kind; if (op == (an_expr_operator_kind)eok_ref_indirect || op == (an_expr_operator_kind)eok_ref_cast || op == (an_expr_operator_kind)eok_ref_dynamic_cast) { /* A reference indirection can be directly changed to be an lvalue. Likewise a reference cast. */ change_directly = TRUE; #if PARENS_IN_IL } else if (op == (an_expr_operator_kind)eok_parens) { /* For a parenthesis node, do a recursive call to change the subtree, then change the eok_parens node directly. */ change_directly = TRUE; expr->variant.operation.operands = conv_xvalue_expr_to_lvalue(expr->variant.operation.operands); #endif /* PARENS_IN_IL */ } /* if */ } /* if */ if (change_directly) { check_assertion(expr->is_xvalue); expr->is_xvalue = FALSE; expr->is_lvalue = TRUE; } else { /* Use a general technique: take a reference to the xvalue and then indirect through it to get an lvalue. */ an_expr_node_ptr ref_expr = add_reference_to_to_node(expr); expr = add_ref_indirection_to_node(ref_expr); } /* if */ return expr; } /* conv_xvalue_expr_to_lvalue */ void conv_xvalue_to_lvalue(an_operand *operand) /* operand is an xvalue. Convert it to an lvalue. */ { an_expr_node_ptr expr, new_expr; check_assertion(is_expression_operand(operand) && is_an_xvalue(operand)); expr = operand->variant.expression; check_assertion(expr->is_xvalue); new_expr = conv_xvalue_expr_to_lvalue(expr); if (new_expr != expr) { check_assertion(identical_types(new_expr->type, expr->type)); operand->variant.expression = new_expr; /* lvalues and xvalues both use the os_glvalue kind, so no change is needed. */ check_assertion(is_a_glvalue(operand)); } /* if */ } /* conv_xvalue_to_lvalue */ void conv_class_operand_to_object_pointer(an_operand *operand) /* Convert a class operand for an object into an operand for a pointer to the object. The operand may be either a glvalue or a prvalue; in the prvalue case, a temporary may be created and initialized with the prvalue, and the address of the temporary returned. It's assumed that the address of the class object may escape. If the operand has a ref class type, convert to a C++/CLI handle instead of a pointer. */ { if (is_error_operand(operand)) { normalize_error_operand(operand); #if CHECKING } else if (!is_class_struct_union_type(operand->type) && !is_template_param_type(operand->type)) { internal_error("conv_class_operand_to_object_pointer: not a class"); #endif /* CHECKING */ } else if (is_a_glvalue(operand)) { if (is_an_xvalue(operand)) { /* The operand is an xvalue. Turn it into an lvalue. */ conv_xvalue_to_lvalue(operand); } /* if */ /* The operand is an lvalue. Take its address. */ take_address_of_or_reference_to_lvalue(operand, /*reference_case=*/FALSE, /*rvalue_reference_case=*/FALSE, /*use_handle_for_ref_class=*/TRUE, /*is_builtin_addressof=*/FALSE, (a_source_position *)NULL); } else if (is_a_prvalue(operand)) { /* The operand is a prvalue. Turn it into an lvalue and take its address. */ conv_class_prvalue_operand_to_lvalue(operand); take_address_of_or_reference_to_lvalue(operand, /*reference_case=*/FALSE, /*rvalue_reference_case=*/FALSE, /*use_handle_for_ref_class=*/TRUE, /*is_builtin_addressof=*/FALSE, (a_source_position *)NULL); } /* if */ } /* conv_class_operand_to_object_pointer */ a_boolean is_an_xvalue(an_operand *operand) /* Return TRUE if the given operand is an xvalue, as defined in C++11. */ { a_boolean is_xvalue = FALSE; if (is_expression_operand(operand) && operand->variant.expression->is_xvalue) { is_xvalue = TRUE; } /* if */ return is_xvalue; } /* is_an_xvalue */ a_boolean rvalue_ref_can_be_bound_to(an_operand *operand) /* Return TRUE if the value category of operand is such that an rvalue reference can be bound to it. For the most part, that means operand is an rvalue (including xvalue). However, for functions, the operand can be an lvalue. */ { a_boolean can_be_bound = FALSE; if (is_an_rvalue(operand)) { can_be_bound = TRUE; } else if (is_a_function_designator(operand) && rvalue_ref_can_be_bound_to_function_lvalue()) { can_be_bound = TRUE; } else if (is_error_operand(operand)) { can_be_bound = TRUE; } /* if */ return can_be_bound; } /* rvalue_ref_can_be_bound_to */ static a_constant_ptr value_of_constant_var_glvalue_expr( an_expr_node_ptr node, a_boolean copy_for_reuse, a_variable_ptr *p_var) /* node is an expression for a glvalue. If it is a glvalue for a constant-valued variable, return a pointer to the constant that is the variable's value. Otherwise, return NULL. If copy_for_reuse is TRUE, copy the constant if necessary so it's suitable for later incorporation in the IL tree, and not merely for short-term inspection. The constant is not copied merely to clear its backing expression; it's assumed the caller will put the constant in an_operand and clear the backing expression in that copy. If p_var is non-NULL and the expression is a glvalue for a variable, *p_var is set to point to the variable. */ { a_constant_ptr con_var_value = NULL; if (p_var != NULL) *p_var = NULL; node = skip_parens(node); check_assertion(node->is_lvalue || node->is_xvalue || is_error_node(node)); if (is_variable_node(node)) { /* The expression is an lvalue for a variable. */ a_variable_ptr var = node_variable(node); if (p_var != NULL) *p_var = var; /* See if the variable has a constant value known at compile time. */ con_var_value = var_constant_value_full(var, copy_for_reuse, /*clear_backing_expr=*/FALSE, /*allow_C_mode_const_var=*/FALSE); } /* if */ return con_var_value; } /* value_of_constant_var_glvalue_expr */ a_constant_ptr value_of_constant_var_lvalue_operand(an_operand *operand) /* operand is an operand for an lvalue. If it is an lvalue for a constant-valued variable, return a pointer to the constant that is the variable's value. Otherwise, return NULL. The constant returned should only be used locally and not linked into the IL tree. */ { a_constant_ptr con_var_value = NULL; if (is_expression_operand(operand)) { con_var_value = value_of_constant_var_glvalue_expr(operand->variant.expression, /*copy_for_reuse=*/FALSE, (a_variable **)NULL); } /* if */ return con_var_value; } /* value_of_constant_var_lvalue_operand */ static a_boolean conv_subscript_in_string_to_char(a_constant *op1, a_constant *op2, a_constant *char_con) /* op1 and op2 are the operands of an lvalue subscripting operation (i.e., op1[op2]) that is being converted to a prvalue. If the operation is something like "abc"[1] such that the value after conversion to an rvalue is a character constant, set *char_con to the character value and return TRUE. Otherwise, return FALSE. This conversion is an extension in both C and C++. */ { a_boolean result = FALSE; a_constant_ptr ptr_op, int_op; /* The operands can appear in either order. */ if (is_pointer_type(op1->type)) { ptr_op = op1; int_op = op2; } else { ptr_op = op2; int_op = op1; } /* if */ if (constant_is_pointer_to_string_literal(ptr_op, (a_constant **)NULL) && is_integral_type(int_op->type) && int_op->kind == (a_constant_repr_kind)ck_integer) { a_type_ptr char_type = f_skip_typerefs(type_pointed_to(ptr_op->type)); if (is_character_type(char_type)) { a_constant_ptr string_constant= ptr_op->variant.address.variant.constant; a_host_large_integer offset; a_boolean ovflo; check_assertion(string_constant->kind==(a_constant_repr_kind)ck_string); /* Check that the offset is within the string. */ offset = value_of_integer_constant(int_op, &ovflo); if (!ovflo && offset >= 0 && (a_targ_size_t)offset < string_constant->variant.string.length) { /* The address is a valid address of a character in the string. Get the value for the character from the string. */ an_integer_kind ikind = char_type->variant.integer.int_kind; a_host_large_integer char_value; char_value = string_constant->variant.string.value[offset]; /* Remove any sign extension. */ char_value &= (long)(~((~(unsigned long)0) << targ_char_bit)); set_integer_constant(char_con, char_value, ikind); /* Sign-extend the character if necessary. */ if (int_kind_is_signed[(int)ikind]) { sign_extend_integer_value(&char_con->variant.integer_value, targ_char_bit); } /* if */ result = TRUE; } /* if */ } /* if */ } /* if */ return result; } /* conv_subscript_in_string_to_char */ a_boolean current_mode_allows_dot_static_folding(an_expr_node_ptr lhs_expr) /* Return TRUE if the current mode/dialect allows the folding of lhs_expr.k to a constant if k is a constant. */ { a_boolean allows_folding = FALSE; if (curr_expr_kind_is_traditional_const()) { /* In constant expressions, we fold such things only as a nonstandard feature. If we decide to do the folding, the left side is guaranteed not to have side effects because it's a constant expression. */ if (current_mode_allows_field_selection_folding()) { allows_folding = TRUE; } /* if */ } else { /* In non-constant expressions, we allow this folding in some modes if the left operand has no side effects. */ if (microsoft_mode && !node_has_side_effects(lhs_expr, (a_boolean *)NULL)) { allows_folding = TRUE; } /* if */ } /* if */ return allows_folding; } /* current_mode_allows_dot_static_folding */ a_constant_ptr fold_constant_base_class_cast(an_expr_node_ptr expr, a_constant_ptr alloc_con) /* If expr is a base-class cast operation whose operand is a class constant, do the slicing and return a constant for the base class. Otherwise, return NULL. If alloc_con is non-NULL, return the base class value there and return alloc_con. Otherwise, allocate a new constant, put the value there, and return the address of the new constant. */ { a_constant_ptr result = NULL; if (is_operation_node(expr) && node_operator_is(expr, eok_base_class_cast)) { an_expr_node_ptr op1 = expr->variant.operation.operands; if (is_constant_node(op1) && node_constant_is(op1, ck_aggregate)) { a_constant_ptr addr_con = local_constant(); a_base_class_ptr bcp = find_base_class_of(op1->type, expr->type); check_assertion(bcp != NULL); /* Make a temporary address so we can use constant_value_at_address. */ set_temporary_address_constant(node_constant(op1), addr_con); addr_con->variant.address.offset = (a_targ_ptrdiff_t)bcp->offset; addr_con->type = make_pointer_type(expr->type); result = constant_value_at_address(addr_con, alloc_con); release_local_constant(&addr_con); } /* if */ } /* if */ return result; } /* fold_constant_base_class_cast */ static inline void simple_glvalue_to_prvalue(an_expr_node *node, a_type_ptr prvalue_node_type) /* Turn a glvalue expression node into a prvalue node by clearing the is_lvalue and is_xvalue flags, recording the original lvalue type, and replacing the node type by prvalue_node_type. */ { node->is_lvalue = FALSE; node->is_xvalue = FALSE; node->orig_lvalue_type = node->type; node->type = prvalue_node_type; } /* simple_glvalue_to_prvalue */ an_expr_node_ptr conv_glvalue_expr_to_prvalue(an_expr_node_ptr node, a_boolean *constant_case, a_constant_ptr *con_value, a_source_position *err_pos) /* node is an expression that is a glvalue. Create an expression for the corresponding prvalue, and return a pointer to it. node can be a function designator. If constant_case is non-NULL, look for cases where conversion to a prvalue produces a constant value (e.g., use of a constant-valued variable in C++), and return *constant_case TRUE if the whole glvalue was folded to a constant. Note that some parts of the glvalue tree might be folded to constants and the overall prvalue might still not be constant, in which case *constant_case is returned FALSE (for example, in "(i ? constvar : j)" constvar would be replaced by its constant value but the overall expression would not fold to a constant). If constant_case is NULL, do not attempt constant replacement. If con_value is non-NULL, and the glvalue folds to a constant, *con_value is set to the constant value and NULL is returned. (In other words, con_value non-NULL is a statement by the caller that he would prefer a constant instead of an expression for the prvalue if it folds to a constant.) In other cases where con_value is non-NULL, *con_value is returned NULL. err_pos is non-NULL to provide a position for errors (folding errors, mostly); if it is NULL, no errors will be issued (e.g., folding will not be attempted if it might produce an error). */ { a_constant_ptr con_expr_value = NULL; a_constant_ptr result_con = local_constant(); a_boolean processed = FALSE; a_boolean template_constant = FALSE; a_type_ptr prvalue_node_type, orig_type = node->type; a_source_position saved_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_range saved_expr_range; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ saved_position = node->position; #if EXTRA_SOURCE_POSITIONS_IN_IL saved_expr_range = node->expr_range; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ if (constant_case != NULL) *constant_case = FALSE; if (con_value != NULL) *con_value = NULL; check_assertion(is_glvalue_node(node) || is_error_node(node)); /* Constant folding can be done only within the expression routines. */ check_assertion(constant_case == NULL || expr_stack != NULL); /* Determine the type for the node after conversion to a prvalue. */ if (is_function_type(orig_type)) { /* Function designator (C) or function lvalue (C++): the conversion to prvalue adds a "pointer to". */ prvalue_node_type = make_pointer_type(orig_type); } else if (is_array_type(orig_type)) { /* In the unlikely event we convert an array glvalue to an array prvalue (we do that with the result of a function call that returns an rvalue reference to array, for example), keep the array type as it is -- don't drop cv-qualifiers on the element type. */ prvalue_node_type = orig_type; } else if (is_managed_nullptr_type(orig_type)) { /* The Microsoft C++/CLI compiler converts the managed nullptr type to std::nullptr_t in a glvalue-to-prvalue conversion. */ prvalue_node_type = standard_nullptr_type(); } else { prvalue_node_type = prvalue_type(orig_type); } /* if */ /* No skip_parens here. Parentheses are handled under the enk_operation case. */ if (is_variable_node(node)) { a_variable_ptr variable = node_variable(node); /* Check if the variable must be instantiated. */ if (!variable->used && !variable->is_nonreal && !variable->is_specialized && variable->template_info != NULL && variable->template_info->template_arg_list != NULL && !symbol_for(variable)->defined) { a_template_instance_ptr tip = template_instance_for_symbol(symbol_for(variable)); if (!tip->instantiation_required && !master_instance_of(tip)->already_instantiated) { instantiate_template_variable(tip, /*is_new=*/FALSE, /*is_use=*/TRUE); } /* if */ } /* if */ if (constant_case != NULL && !C_mode()) { /* Look for constant-valued variables in C++. */ con_expr_value = value_of_constant_var_glvalue_expr( node, /*copy_for_reuse=*/TRUE, &variable); if (con_expr_value != NULL) { con_expr_value->type = prvalue_node_type; if (constant_is(con_expr_value, ck_template_param)) { /* This is a dependent reference to a variable. Set up to create a tpck_expression constant for the expression below. */ template_constant = TRUE; con_expr_value = NULL; if (in_file_scope(node) && !in_file_scope(variable)) { /* Need to allocate a node in the appropriate region so that a local expr node reference will be used in the constant. */ a_memory_region_number region_to_switch_back_to; a_scope_depth func_depth = get_depth_innermost_function_scope(); check_assertion(func_depth > NO_SCOPE_DEPTH); switch_to_scope_region(func_depth, ®ion_to_switch_back_to); node = var_rvalue_expr(variable); switch_back_to_original_region(region_to_switch_back_to); } else { /* Just make the original node an rvalue. */ simple_glvalue_to_prvalue(node, prvalue_node_type); } /* if */ } else { /* Below, we'll record the expression for the constant, so make the prvalue version of the expression. */ an_expr_node_ptr orig_node = node; node = expr_to_record_for_variable(variable, /*is_lvalue=*/FALSE); if (node != NULL && is_variable_node(node)) { node->variant.variable.name_reference = orig_node->variant.variable.name_reference; orig_node->variant.variable.name_reference = NULL; } /* if */ } /* if */ } /* if */ } /* if */ } else if (is_operation_node(node)) { 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_boolean local_constant_case; a_boolean *allow_folding = NULL; /* No skip_parens on the operands. We want to be able to unlink/relink the original operands. Parentheses are handled like any other operation. */ /* Set allow_folding for recursive calls. It just needs to be non-NULL if we want to allow constant folding, and it has to point to something that it is harmless to alter. */ if (constant_case != NULL) allow_folding = &local_constant_case; if (node->variant.operation.returns_lvalue_instead_of_usual_rvalue) { /* Operation that returns an lvalue where the C case would return an rvalue. Convert it to the C form. */ if (op == (an_expr_operator_kind)eok_question) { /* "?" operator. Convert the second and third operands to prvalues. */ an_expr_node_ptr op3 = op2->next; op1->next = NULL; op2->next = NULL; /* Expressions produced by conv_prvalue_expr_to_lvalue can have a throw as one arm of the lvalue. */ if (!node_is(skip_parens(op2), enk_throw)) { op2 = conv_glvalue_expr_to_prvalue(op2, allow_folding, (a_constant_ptr *)NULL, err_pos); } /* if */ if (!node_is(skip_parens(op3), enk_throw)) { op3 = conv_glvalue_expr_to_prvalue(op3, allow_folding, (a_constant_ptr *)NULL, err_pos); } /* if */ op1->next = op2; op2->next = op3; /* If all three operands are now constant, the overall result is constant. */ op1 = skip_parens(op1); op2 = skip_parens(op2); op3 = skip_parens(op3); if (allow_folding != NULL && is_constant_node(op1) && ((is_constant_node(op2) && is_constant_node(op3)) || node_constant_is(op1, ck_template_param))) { a_constant_ptr con1 = node_constant(op1); if (constant_bool_value_known_at_compile_time(con1) && is_constant_node(op2) && is_constant_node(op3)) { /* The operation can be folded to the second or third operand. */ an_expr_node_ptr result = is_false_constant(con1) ? op3 : op2; con_expr_value = node_constant(result); } else if (constant_is(con1, ck_template_param) || node_constant_is(op2, ck_template_param) || node_constant_is(op3, ck_template_param)) { template_constant = TRUE; } /* if */ } /* if */ } else if (op == (an_expr_operator_kind)eok_comma) { /* Comma operator. Apply the transformation to the second operand of the ",". */ op1->next = conv_glvalue_expr_to_prvalue(op2, allow_folding, (a_constant_ptr *)NULL, err_pos); } else { /* The operation is an assignment or a prefix ++/-- that returns an lvalue. All that's needed is the code below that changes it to an operation returning a prvalue. */ } /* if */ processed = TRUE; node->variant.operation.returns_lvalue_instead_of_usual_rvalue = FALSE; simple_glvalue_to_prvalue(node, prvalue_node_type); } else { /* Operations that don't have returns_lvalue_instead_of_usual_rvalue set. */ switch (op) { case eok_dot_field: case eok_points_to_field: /* An object expression designating a literal class type initialized to a constant value allows use of a member value as a constant. */ if (constexpr_enabled && allow_folding != NULL && fold_constexpr_member_selection(node, result_con)) { con_expr_value = alloc_shareable_constant(result_con); simple_glvalue_to_prvalue(node, prvalue_node_type); processed = TRUE; } /* if */ break; case eok_pm_field: { a_boolean saved_is_lvalue = node->is_lvalue; a_boolean saved_is_xvalue = node->is_xvalue; /* Set the node to be a prvalue for the attempt at folding. */ node->is_lvalue = FALSE; node->is_xvalue = FALSE; if (constexpr_enabled && allow_folding != NULL && fold_constexpr_expr(node, result_con, /*is_constant_evaluated=*/FALSE, /*force_prvalue=*/FALSE)) { /* x.*y, where x is a constexpr object. */ con_expr_value = alloc_shareable_constant(result_con); node->orig_lvalue_type = orig_type; node->type = prvalue_node_type; processed = TRUE; } else { /* The expression could not be folded; restore the original value category. */ node->is_lvalue = saved_is_lvalue; node->is_xvalue = saved_is_xvalue; } /* if */ } break; case eok_indirect: op1 = skip_parens(op1); if (allow_folding != NULL && (con_expr_value = constant_value_addressed_by_node(node)) != NULL) { /* Indirection through a constexpr pointer that points to an object with a constant value. Use that value as the result of the expression. */ simple_glvalue_to_prvalue(node, prvalue_node_type); processed = TRUE; } else if (allow_folding != NULL && is_constant_node(op1)) { if (node_constant_is(op1, ck_template_param)) { /* A dependent expression. Treat it as a prvalue template constant. */ template_constant = TRUE; simple_glvalue_to_prvalue(node, prvalue_node_type); processed = TRUE; } else if (gnu_mode || microsoft_mode) { /* The GNU compilers accept an expression like *&(S){{0}} as a constant. The Microsoft compiler accepts a unary * applied to an address constant designating a variable with a constant value. */ a_constant_ptr cp = node_constant(op1); a_variable_ptr var; if (con_is_exact_addr_of_variable(cp, &var, /*array_decay_allowed=*/TRUE) && (con_expr_value = var_constant_value(var)) != NULL) { if (microsoft_mode || (gnu_mode && var->is_compound_literal)) { /* Use the constant as the value of the expression. */ simple_glvalue_to_prvalue(node, prvalue_node_type); processed = TRUE; } else { /* The expression cannot be folded. */ con_expr_value = NULL; } /* if */ } /* if */ } /* if */ } /* if */ break; case eok_ref_indirect: if (allow_folding != NULL && (con_expr_value = constant_value_addressed_by_node(node)) != NULL) { /* Indirection through a constexpr reference that refers to an object with a constant value. Use that value as the result of the expression. */ simple_glvalue_to_prvalue(node, prvalue_node_type); processed = TRUE; } /* if */ break; case eok_subscript: if (allow_folding != NULL && !strict_ansi_mode && op2 != NULL && op2->next == NULL) { op1 = skip_parens(op1); op2 = skip_parens(op2); if (is_constant_node(op1) && is_constant_node(op2)) { /* Something like "abc"[1] can be folded to the character value. */ if (conv_subscript_in_string_to_char(node_constant(op1), node_constant(op2), result_con)) { con_expr_value = alloc_shareable_constant(result_con); simple_glvalue_to_prvalue(node, prvalue_node_type); processed = TRUE; } else if (gcc_version_is(>=80000) || clangc_version_is(>=170000)) { /* For example: int const arr[] = { 0, 1 }; struct S { int i; } S s = { arr[1] }; is accepted by some GCC and Clang versions. Note that just passing "force_prvalue=TRUE" doesn't currently work in C mode. Instead, we just temporarily treat the node as a prvalue and attempt to fold it. */ a_boolean folded; node->is_lvalue = FALSE; folded = fold_constexpr_expr(node, result_con, /*is_constant_evaluated=*/FALSE, /*force_prvalue=*/FALSE); node->is_lvalue = TRUE; if (folded) { con_expr_value = alloc_shareable_constant(result_con); simple_glvalue_to_prvalue(node, prvalue_node_type); processed = TRUE; } /* if */ } /* if */ } /* if */ } /* if */ break; case eok_cli_subscript: /* C++/CLI array subscript. Can never be folded to a constant. */ simple_glvalue_to_prvalue(node, prvalue_node_type); processed = TRUE; break; case eok_dot_static: case eok_points_to_static: /* Static field selection operator. Apply the transformation to the second operand, unless the first operand is template-dependent. */ if (expr_is_instantiation_dependent(op1)) { /* Wait until substitution to perform the transformation. */ simple_glvalue_to_prvalue(op2, prvalue_node_type); simple_glvalue_to_prvalue(node, prvalue_node_type); processed = TRUE; break; } /* if */ op2 = conv_glvalue_expr_to_prvalue(op2, allow_folding, (a_constant_ptr *)NULL, err_pos); op1->next = op2; /* No skip_parens needed on op2. */ if (allow_folding != NULL && is_constant_node(op2) && (current_mode_allows_dot_static_folding(op1) || (gpp_mode && cpp11_mode && !clang_mode && !node_has_side_effects(op1, (a_boolean *)NULL)))) { /* In modes that allow folding of static field selection to a constant, do so. */ check_assertion(!node_has_side_effects(op1, (a_boolean *)NULL) || is_error_node(op1)); con_expr_value = node_constant(op2); } /* if */ simple_glvalue_to_prvalue(node, prvalue_node_type); if (con_expr_value != NULL && curr_il_region_number == file_scope_region_number && innermost_function_scope != NULL) { /* For an expression scanned within a function body whose constant will be at file scope, like an array bound, discard the selector in the backing expression because it might reference "this" or local variables. */ node = con_expr_value->expr; } /* if */ processed = TRUE; break; #if GNU_EXTENSIONS_ALLOWED case eok_gnu_min: case eok_gnu_max: /* GNU C++ minimum and maximum operators. */ op1->next = NULL; op1 = conv_glvalue_expr_to_prvalue(op1, allow_folding, (a_constant_ptr *)NULL, err_pos); op2 = conv_glvalue_expr_to_prvalue(op2, allow_folding, (a_constant_ptr *)NULL, err_pos); node->variant.operation.operands = op1; op1->next = op2; op1 = skip_parens(op1); op2 = skip_parens(op2); if (allow_folding != NULL && err_pos != NULL && is_constant_node(op1) && is_constant_node(op2)) { /* Both operands are now constant so fold to a constant result. */ a_boolean did_not_fold; expr_binary_operation(op, node_constant(op1), node_constant(op2), op1->type, result_con, &did_not_fold, &template_constant, err_pos); if (template_constant) { /* One or both of the operands is template-dependent. The constant produced will be a ck_template_param pointing to the expression (see below). */ } else { check_assertion(!did_not_fold); con_expr_value = alloc_shareable_constant(result_con); } /* if */ } /* if */ simple_glvalue_to_prvalue(node, prvalue_node_type); processed = TRUE; break; #endif /* GNU_EXTENSIONS_ALLOWED */ case eok_base_class_cast: /* Derived-to-base class: Perform address folding if needed before marking the cast result as an rvalue. */ if (constexpr_enabled && allow_folding != NULL) { con_expr_value = constant_value_addressed_by_node(node); } /* if */ simple_glvalue_to_prvalue(node, prvalue_node_type); processed = TRUE; break; case eok_parens: /* For parentheses, turn the underlying expression into a prvalue, and re-mark the parenthesis node as a prvalue. We don't record a separate "orig_lvalue_type" for the eok_parens node. */ op1 = conv_glvalue_expr_to_prvalue(op1, allow_folding, (a_constant_ptr *)NULL, err_pos); node->variant.operation.operands = op1; op1 = skip_parens(op1); if (allow_folding != NULL && is_constant_node(op1)) { /* The operand is now constant so the overall expression is constant. */ con_expr_value = node_constant(op1); } /* if */ node->is_lvalue = node->is_xvalue = FALSE; node->type = op1->type; processed = TRUE; break; case eok_lvalue_cast: { /* An lvalue cast becomes a simple cast on the operand, after the latter is turned into a prvalue. */ op1 = conv_glvalue_expr_to_prvalue(op1, allow_folding, (a_constant_ptr*)NULL, err_pos); node->variant.operation.operands = op1; op1 = skip_parens(op1); if (allow_folding != NULL && err_pos != NULL && is_constant_node(op1)) { /* The operand is now constant so try to fold the cast to a constant. */ a_boolean did_not_fold; copy_constant(node_constant(op1), result_con); expr_type_change_constant(result_con, prvalue_node_type, /*is_implicit_cast=*/FALSE, /*check_cast_access=*/FALSE, /*check_ambiguity=*/FALSE, /*is_reinterpret_cast=*/FALSE, /*maintain_expression=*/TRUE, &did_not_fold, err_pos); check_assertion(!did_not_fold); con_expr_value = alloc_shareable_constant(result_con); } /* if */ set_node_operator(node, (an_expr_operator_kind)eok_cast, prvalue_node_type, /*is_lvalue=*/FALSE, op1); node->orig_lvalue_type = orig_type; } /* if */ processed = TRUE; break; case eok_ref_cast: /* eok_ref_cast is handled the same as eok_adjust_lvalue, but only allow the conversion to a constant if the underlying entity is a temporary or if the types are reference-related. See core issue 1480. Normally, the underlying entity has an address that we must preserve (e.g., in an xvalue). */ if (constexpr_enabled && ((node_is(op1, enk_temp_init) && op1->variant.init.dynamic_init->has_temporary_lifetime) || are_reference_related(orig_type, op1->type))) { goto lvalue_adjust; } /* if */ break; case eok_lvalue_adjust: lvalue_adjust: /* If the operand of an lvalue adjust can be converted to a constant, the result is the type-adjusted constant. Otherwise, it's rvalueable so we go to the general case. */ if (allow_folding != NULL && is_glvalue_node(op1) && !node->variant.operation.is_reinterpret_cast && are_reference_related(orig_type, op1->type)) { con_expr_value = constant_value_addressed_by_node(op1); if (con_expr_value != NULL) { a_boolean did_not_fold; copy_constant(con_expr_value, result_con); expr_type_change_constant(result_con, prvalue_node_type, /*is_implicit_cast=*/FALSE, /*check_cast_access=*/FALSE, /*check_ambiguity=*/FALSE, /*is_reinterpret_cast=*/FALSE, /*maintain_expression=*/TRUE, &did_not_fold, err_pos); if (!did_not_fold) { con_expr_value = alloc_shareable_constant(result_con); simple_glvalue_to_prvalue(node, prvalue_node_type); processed = TRUE; } /* if */ } /* if */ } /* if */ break; case eok_lvalue: /* A node that forces an rvalue to be considered to be an lvalue, in a prototype instantiation. */ /* Return the prvalue operand of the eok_lvalue operation. */ node = op1; check_assertion(!is_glvalue_node(node)); processed = TRUE; op1 = skip_parens(op1); if (allow_folding != NULL && is_constant_node(op1) && con_value != NULL) { /* The result is a constant expression, and the caller can take a constant result directly. (If the caller won't take a constant result directly, we'd just create another expression node below to return the constant, so we might as well use the one we have.) */ con_expr_value = node_constant(op1); /* Note that in this case we don't record the expression for the constant. It's an enk_constant node, which wouldn't be useful. */ node = NULL; } /* if */ break; case eok_unbox: case eok_unbox_lvalue: /* You can get a prvalue by setting is_lvalue to FALSE to cause a fetch from the boxed entity, but the operation is not rvalueable. */ simple_glvalue_to_prvalue(node, prvalue_node_type); processed = TRUE; break; default: break; } /* switch */ } /* if */ } else if (node_is(node, enk_temp_init)) { /* If the given node is a generated temporary initialized with the result of a comma operation or an rvalue expression of the right type, just get back that expression. */ a_dynamic_init_ptr dip = node->variant.init.dynamic_init; if (dip->kind == (a_dynamic_init_kind)dik_expression && !(dip->is_result_for_class_rvalue_question_mark || dip->is_result_for_comma_operator)) { an_expr_node_ptr dip_expr = dip->variant.expression; if (!dip_expr->is_lvalue && !dip_expr->is_xvalue && is_generated_dynamic_init(dip) && identical_types_full(dip_expr->type, prvalue_node_type, ITF_EXACT_EQUIVALENCE)) { /* dip_expr is going to be used as the prvalue version of the enk_temp_init node. Record the fact that there is a notional glvalue-to-prvalue conversion involved. */ dip_expr->was_lvalue_temp_initializer = TRUE; reclaim_node_if_possible(node); node = dip_expr; processed = TRUE; } /* if */ } /* if */ } else if (node_is(node, enk_object_lifetime)) { /* We don't expect an lvalue-to-rvalue conversion to be done after the enk_object_lifetime node has been added to the top of an expression. */ unexpected_condition(); #if BUILTIN_FUNCTIONS_ENABLED } else if (node_is(node, enk_builtin_choose_expr)) { /* Apply the conversion to the selected node. */ an_expr_node_ptr selected, converted; a_boolean local_constant_case; a_boolean *allow_folding = NULL; selected = node->variant.builtin_choose_expr.operands->next; if (!node->variant.builtin_choose_expr.choose_first) { selected = selected->next; } /* if */ if (constant_case != NULL) allow_folding = &local_constant_case; converted = conv_glvalue_expr_to_prvalue(selected, allow_folding, (a_constant_ptr *)NULL, err_pos); if (!node->variant.builtin_choose_expr.choose_first) { node->variant.builtin_choose_expr.operands->next->next = converted; } else { converted->next = selected->next; node->variant.builtin_choose_expr.operands->next = converted; } /* if */ if (allow_folding != NULL && is_constant_node(converted)) { /* The operand is now constant so the overall expression is constant. */ con_expr_value = node_constant(converted); } /* if */ simple_glvalue_to_prvalue(node, converted->type); processed = TRUE; #endif /* BUILTIN_FUNCTIONS_ENABLED */ } else if (node_is(node, enk_c11_generic)) { /* Apply the conversion to the selected node. */ an_expr_node_ptr selected, *p_slot, converted; a_boolean local_constant_case; a_boolean *allow_folding = NULL; p_slot = &node->variant.c11_generic.operands; selected = node->variant.c11_generic.result; while (*p_slot != selected) p_slot = &(*p_slot)->next; *p_slot = selected->next; if (constant_case != NULL) allow_folding = &local_constant_case; converted = conv_glvalue_expr_to_prvalue(selected, allow_folding, (a_constant_ptr *)NULL, err_pos); node->variant.c11_generic.result = converted; converted->next = *p_slot; *p_slot = converted; if (allow_folding != NULL && is_constant_node(converted)) { /* The operand is now constant so the overall expression is constant. */ con_expr_value = node_constant(converted); } /* if */ simple_glvalue_to_prvalue(node, converted->type); processed = TRUE; } /* if */ /* At this point, -- If con_expr_value != NULL, the expression has a constant value. node will also have been set to the prvalue version of the expression, to be recorded in the constant, or NULL if no expression should be recorded. constant_case will be non-NULL. -- If template_constant is TRUE, the expression has a constant result that is template-dependent. node will have been set to the prvalue version of the expression, to be used under a template parameter constant. con_expr_value will be NULL. constant_case will be non-NULL. -- If processed is TRUE (and the above do not apply), node will have been set to the prvalue version of the expression. */ if (con_expr_value != NULL && expr_has_reused_value_init(node)) { /* The expression initializes a temporary whose value is reused elsewhere in the enclosing expression. Using the constant value in place of the expression would leave those reuses without a value, as in struct E { bool operator==(E) const; bool operator<(E) const; }; struct S { E e; std::strong_ordering operator<=>(const S &) const = default; }; where the two comparisons of the synthesized member comparison share the address of the member and the second operand of the "==" folds to the value of the empty class E. Keep the expression instead. */ con_expr_value = NULL; } /* if */ if (con_expr_value != NULL) { /* The expression is constant-valued. */ if (node != NULL && curr_expr_kind_is_one_in_which_const_exprs_are_recorded()) { /* Record the expression in the constant. The expression "node" has already been converted to prvalue form. */ check_assertion(!is_glvalue_node(node)); /* Make a distinct copy of the constant so we can change it. */ con_expr_value = alloc_unshared_constant(con_expr_value); con_expr_value->expr = node; /* Restore the original expression position. */ node->position = saved_position; #if EXTRA_SOURCE_POSITIONS_IN_IL node->expr_range = saved_expr_range; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } else if (con_expr_value->expr != NULL) { /* We need to clear the expression pointer in the constant (someone decided we don't want to record the expression, possibly because of memory region problems). */ a_constant_ptr con_copy = local_constant(); copy_constant(con_expr_value, con_copy); con_copy->expr = NULL; con_expr_value = alloc_shareable_constant(con_copy); release_local_constant(&con_copy); } /* if */ node = NULL; } else if (template_constant) { /* The result is a template-dependent constant. We have a correct expression tree now, so put it under a ck_template_param constant and return that. */ check_assertion(node != NULL && !is_glvalue_node(node)); make_template_param_expr_constant(node, result_con); /* The expression is in the constant now, so clear the variable to indicate that we have handled the expression. */ node = NULL; if (con_value == NULL) { /* The caller wants an expression node returned, so let the allocation be done by alloc_node_for_constant (below). */ con_expr_value = result_con; } else { /* The caller wants the constant address returned, so allocate it. */ con_expr_value = alloc_shareable_constant(result_con); } /* if */ } else if (processed) { /* The prvalue node has already been produced. */ check_assertion(node != NULL && !is_glvalue_node(node)); } else { /* Not a special case. Just clear the is_lvalue and is_xvalue flags and change the type to the prvalue type. */ #if CHECKING /* Make sure this is a node that can be turned into a prvalue by clearing the flags. (Mostly these are "rvalueable" nodes, where a prvalue state represents a "load". In some cases, a lambda may have been transformed from a prvalue to a glvalue, but the reverse transformation isn't really considered a "load" since it is the original state of the expression.) */ if (!is_rvalueable_node(node) && !node_is(node, enk_lambda)) { #if DEBUG fprintf(f_debug, "\n"); db_expression(node); #endif /* DEBUG */ unexpected_condition_str("conv_glvalue_expr_to_prvalue: bad expr"); } /* if */ #endif /* CHECKING */ simple_glvalue_to_prvalue(node, prvalue_node_type); if (node_is(node, enk_lambda)) { /* A lambda started off as a prvalue and so we don't record an "original lvalue" type because that can cause some other components to assume a "load" took place. */ node->orig_lvalue_type = NULL; } /* if */ } /* if */ if (con_expr_value != NULL) { /* The expression is constant-valued. */ check_assertion(constant_case != NULL); *constant_case = TRUE; check_assertion(node == NULL); /* Check node was previously handled. */ /* Determine how to return the constant value to the caller. */ if (con_value != NULL) { /* The caller wants the constant instead of an expression node for the constant. */ *con_value = con_expr_value; node = NULL; } else { /* The caller wants an expression node for the constant. */ node = alloc_node_for_constant(con_expr_value); node->orig_lvalue_type = orig_type; } /* if */ } /* if */ release_local_constant(&result_con); return node; } /* conv_glvalue_expr_to_prvalue */ an_expr_node_ptr conv_glvalue_expr_to_prvalue_external( an_expr_node_ptr node, a_boolean *constant_case, a_constant_ptr *con_value, a_source_position *err_pos, a_ctws_options_set options) /* Interface for calling conv_glvalue_expr_to_prvalue from outside of the expression routines when one wants to pass constant_case non-NULL (which is normally forbidden for "outside" calls). Pushes an expression stack entry around the call. options is a set of copy_type_with_substitution options. */ { an_expr_node_ptr expr; an_expr_stack_entry expr_stack_entry; an_expression_kind kind = (options & CTWS_NON_CONSTANT_EXPR) ? (an_expression_kind)ek_normal : (an_expression_kind)ek_init_constant; push_expr_stack(kind, &expr_stack_entry, /*force_object_lifetime=*/FALSE, /*suppress_object_lifetime=*/FALSE); expr = conv_glvalue_expr_to_prvalue(node, constant_case, con_value, err_pos); pop_expr_stack(); return expr; } /* conv_glvalue_expr_to_prvalue_external */ static a_boolean incomplete_glvalue_type_okay_for_prvalue(a_type_ptr utp) /* The given type is the type of a glvalue operand after skipping typerefs. Return TRUE if it is okay for it to be an incomplete type when converted to a prvalue. This is TRUE for nonlocal nonreal class types. E.g.: template struct S; template void g(S *p) { static_cast(*p); // Okay. struct L; // Nonreal local type. static_cast(*(L*)p); // Error. } Clang, GCC, and MSVC also allow this in the function prototype scope of a function template declaration. For example: template struct S { static bool f(S); template friend auto g(S p)->decltype(S::f(p)) { throw 42; } }; S si; // Ordinarily, S::f(p) would be an error because S is still // incomplete, but this is generally accepted. */ { return is_immediate_class_type(utp) && ((utp->variant.class_struct_union.is_nonreal_class && !utp->source_corresp.is_local_to_function) || (!strict_ansi_mode && scope_is(&scope_stack_top(), sck_func_prototype) && scope_is(&scope_stack_top()-1, sck_template_declaration))); } /* incomplete_glvalue_type_okay_for_prvalue */ void conv_glvalue_to_prvalue(an_operand *operand) /* Convert a glvalue operand to a prvalue operand. See section 6.3.2.1 of the C99 standard and [conv.lval] of the C++ standard. If the operand is not a glvalue, it is left alone. Function designators are not considered lvalues here and are left alone. Note that this routine does not add a copy when it converts a class glvalue to a prvalue in C++ mode; the standard requires that, but it's actually wanted only in some limited cases so we don't do it here. */ { an_expr_node_ptr node; an_operand orig_operand; a_type_ptr unqual_operand_type; a_boolean constant_case = FALSE; a_constant_ptr con_value = NULL; /* Ignore non-glvalues. */ if (is_a_glvalue(operand)) { /* A glvalue becomes a prvalue. */ a_boolean volatile_fetch; unqual_operand_type = operand->type; volatile_fetch = is_volatile_qualified_type(unqual_operand_type); unqual_operand_type = skip_typerefs(unqual_operand_type); #if CHECKING /* Array glvalues are not allowed. */ if (unqual_operand_type->kind == (a_type_kind)tk_array) { internal_error("conv_glvalue_to_prvalue: array glvalue"); } /* if */ #endif /* CHECKING */ /* Save the operand's type and source position*/ orig_operand = *operand; if (!operand->pending_capture) { /* Change simple "reference" references to "use" references. However, do not do that for captured operands since the use in a lambda isn't "immediate" and could result in a spurious used-before-set warning otherwise. */ /* Note that what we want to avoid here is changing "modified" references to "use" references, as would happen for references surviving from an lvalue-returning assignment. */ change_some_ref_kinds(operand->ref_entries_list, SRK_REFERENCE, SRK_USE); /* Change the kind in the reference entry for a subscripted array from an address-taken entry to a simple "use" reference. */ change_some_ref_kinds(operand->ref_entries_list, SRK_ADDRESS_TAKEN, SRK_USE); } /* if */ /* Instantiate the type if it is a template. */ complete_type_is_needed(unqual_operand_type); if (is_error_operand(operand)) { /* Aside from the usual function, the normalization here makes sure the operand is not a glvalue anymore. */ normalize_error_operand(operand); } else if (unqual_operand_type->incomplete && !is_managed_nullptr_type(unqual_operand_type) && (C_mode() ? !type_is(unqual_operand_type, tk_void) : !incomplete_glvalue_type_okay_for_prvalue( unqual_operand_type))) { /* Converting a glvalue with incomplete type to a prvalue is an error in C++ ([conv.lval]), and undefined behavior in C (C99 6.3.2.1). We treat it as an error in C mode except when the lvalue has (possibly cv-qualified) void type. That latter qualification is needed to pass DR 106. In addition, the C++/CLI managed nullptr type, although prohibited as the type of an object, etc., is acceptable in an lvalue-to-rvalue conversion. */ expr_issue_incomplete_type_diag(&operand->position, unqual_operand_type); conv_to_error_operand(operand); } else if (is_template_param_expression_constant_operand(operand)) { /* A template param constant operand will retain the state of the original operand, rather than being set to os_prvalue. Always allow conversion if requested. */ operand->state = (an_operand_state)os_prvalue; } else { a_boolean pending_capture = operand->pending_capture; using_lvalue(operand); check_assertion(is_expression_operand(operand)); if (gcc_mode && gcc_const_variables_allowed) { /* GCC allows const variables to be used in constant expressions in some C-mode cases, although often only with -O1. Since the front end has no -Oxyz options, the behavior is controlled by a separate gcc_const_variables_allowed variable. */ a_variable_ptr var; if (operand_is_lvalue_for_variable(operand, &var) && is_potentially_constant_valued_variable(var) && (con_value = var_constant_value_full( var, /*copy_for_reuse=*/FALSE, /*clear_backing_expr=*/FALSE, /*allow_C_mode_const_var=*/TRUE)) != NULL) { if ((curr_expr_kind_is_const() || expr_stack->is_vla_dimension_expression || (curr_expr_kind_is(ek_sizeof) && expr_stack->favor_constant_result))) { /* Issue a warning in contexts where this treatment makes a significant difference. The test for ek_sizeof and favor_constant_result is to identify the operand of a __builtin_constant_p call. */ expr_pos_warning(ec_const_var_in_C_const_expr, &operand->position); } /* if */ constant_case = TRUE; con_value = alloc_unshared_constant(con_value); con_value->expr = var_rvalue_expr(var); /* The folded constant variable is never treated as a null pointer constant. */ con_value->null_pointer_constant_ruled_out = TRUE; } /* if */ } /* if */ node = make_node_from_operand(operand, /*no_rewrite=*/TRUE); check_assertion(is_glvalue_node(node)); if (!constant_case) { /* Convert the expression to a prvalue. */ node = conv_glvalue_expr_to_prvalue(node, &constant_case, &con_value, &operand->position); } /* if */ if (constant_case) { /* The value of the expression is a constant, e.g., a constant-valued variable in C++ has been replaced by its value. Make a constant operand instead of the expression operand. */ check_assertion(con_value != NULL); /* Add the reference kind that indicates a use only for the purpose of extracting a constant value. */ change_some_ref_kinds(operand->ref_entries_list, SRK_USE, (SRK_USE | SRK_CONST_VALUE_USE)); if (!is_error_constant(con_value)) { make_constant_operand(con_value, operand); } else { make_error_operand(operand); } /* if */ } else if (curr_expr_kind_is_traditional_const()) { /* An lvalue cannot be converted to an rvalue in a pre-C++11 constant expression. */ if ((gcc_version_is(>= 80000) || clangc_version_is(>= 170000)) && is_expression_operand(operand)) { (void)expr_interpret_expression_operand( operand, /*must_be_constant=*/TRUE, /*is_constant_evaluated=*/FALSE, /*force_prvalue=*/TRUE); } else if (!is_constant_operand(operand)) { error_in_operand(ec_expr_not_constant, operand); } /* if */ } else if (!constexpr_enabled && !potential_gnu_ignored_object_expr(node, curr_token) && construct_not_allowed_in_cpp11_constant_expr( ec_expr_not_constant, &operand->position)) { /* A glvalue cannot be converted to a prvalue in a C++11 constant expression. */ conv_to_error_operand(operand); } else { /* Normal case: not constant-valued, not a constant expression. */ node->volatile_fetch = volatile_fetch; make_expression_operand(node, operand); if (pending_capture) { operand->pending_capture = TRUE; } /* if */ if (expr_stack->fold_prvalue_if_possible) { /* Fold the expression if possible. This is not just an optimization because it may determine if underlying variables are "used": If folding succeeds, and the resulting value no longer refers to (the address of) a variable involved, that variable isn't used (i.e., no definition of the variable is needed for this particular sub- expression). */ if (expr_interpret_expression_operand( operand, /*must_be_constant=*/FALSE, /*is_constant_evaluated=*/FALSE)) { #if MICROSOFT_EXTENSIONS_ALLOWED a_constant_ptr con = &operand->variant.constant; if (microsoft_mode && con->null_pointer_constant_ruled_out && constant_is(con, ck_integer) && cmplit_integer_constant(con, (a_host_large_integer)0) == 0) { /* Microsoft compilers accept a broader range of "null pointer" forms. After folding, restore the null pointer flag if needed. */ a_boolean is_nullptr_constant; a_constant_ptr null_con; adjust_constant_operand_info_for_microsoft_null_pointer_test( &orig_operand, &is_nullptr_constant, &null_con, (an_expr_node_ptr*)NULL); if (is_nullptr_constant) { con->null_pointer_constant_ruled_out = FALSE; } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ } /* if */ if (operand->type != orig_operand.type && unqual_operand_type->kind == (a_type_kind)tk_template_param && unqual_operand_type->variant.template_param.kind == (a_template_param_type_kind)tptk_unknown) { /* Sometimes the operand type is forced to tk_unknown to ensure the operand is treated as template dependent, even though the actual type is known to the front end. Restore the template dependence. */ operand->type = orig_operand.type; } /* if */ } /* if */ } /* if */ /* Restore the operand's source position. */ restore_operand_details(operand, &orig_operand); /* The ref_entries_list is cleared because it should only contain information on glvalue addresses. */ operand->ref_entries_list = NULL; restore_operand_form_of_name_reference(operand, &orig_operand); if (!constant_case) { /* An lvalue-to-rvalue conversion rules out a constant expression. */ rule_out_expr_kinds(ROEK_CONSTANT, operand); } /* if */ expr_stack->fold_prvalue_if_possible = FALSE; } /* if */ } /* conv_glvalue_to_prvalue */ void make_lvalue_operand_from_compound_constant(a_constant_ptr constant, an_operand *operand) /* The given constant has nonscalar type (presumably the result of a compound literal). Make the given operand a variable initialized with that constant. The source positions in the operand are set to the current token position. */ { /* Use a static temporary so it can be statically initialized. */ a_variable_ptr temp_var = alloc_temporary_variable(constant->type, /*force_static=*/TRUE); temp_var->is_compound_literal = TRUE; /* The operand is an lvalue for the temporary. */ make_lvalue_variable_operand(temp_var, &pos_curr_token, end_position_or_null(&end_pos_curr_token), operand, (a_ref_entry_ptr)NULL); temp_var->init_kind = (an_init_kind)initk_static; if (!in_file_scope(constant)) { /* Copy the constant to the file scope memory region. This copies the whole tree for aggregate constants. */ a_memory_region_number region_to_switch_back_to; switch_to_file_scope_region(®ion_to_switch_back_to); constant = copy_unshared_constant(constant); switch_back_to_original_region(region_to_switch_back_to); if (gnu_mode || clang_mode) { /* GCC and Clang treat local-scope compound literals as prvalues, which are therefore immutable "in principle". Model this by making the variable "const" but keeping the original type for the operand. */ a_type_ptr tp = temp_var->type; temp_var->type = make_qualified_type(tp, TQ_CONST); } /* if */ } /* if */ temp_var->initializer.constant = constant; } /* make_lvalue_operand_from_compound_constant */ an_expr_node_ptr conv_array_expr_to_pointer(an_expr_node_ptr node) /* node is an expression for an lvalue or rvalue array. Do the array-to-pointer decay on it, and return a pointer to the decayed expression. */ { a_boolean processed = FALSE; a_type_ptr ptr_type = type_after_array_to_pointer_transformation(node->type); /* No skip_parens here; parentheses are handled as an operator below. */ if (is_operation_node(node)) { 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; if (node->variant.operation.returns_lvalue_instead_of_usual_rvalue) { /* Certain cases of lvalue-returning operations are transformed by transforming the operands under the operation. */ if (op == (an_expr_operator_kind)eok_question) { an_expr_node_ptr op3 = op2->next; /* Transform a case like (i ? "ab" : "cd") by transforming the second and third operands. */ op2->next = NULL; if (skip_parens(op2)->kind != (an_expr_node_kind)enk_throw) { op2 = conv_array_expr_to_pointer(op2); } /* if */ if (skip_parens(op3)->kind != (an_expr_node_kind)enk_throw) { op3 = conv_array_expr_to_pointer(op3); } /* if */ op1->next = op2; op2->next = op3; } else if (op == (an_expr_operator_kind)eok_comma) { /* Transform a case like (i , "cd") by transforming the second operand. */ op2 = conv_array_expr_to_pointer(op2); op1->next = op2; } /* if */ node->variant.operation.returns_lvalue_instead_of_usual_rvalue = FALSE; processed = TRUE; } else { /* Operations that don't have returns_lvalue_instead_of_usual_rvalue set. */ switch (op) { case eok_dot_static: case eok_points_to_static: /* Transform a case like x.static_array_var by transforming the second operand. */ op2 = conv_array_expr_to_pointer(op2); op1->next = op2; processed = TRUE; break; case eok_parens: /* Parentheses -- do a recursive call on the operand. */ op1 = conv_array_expr_to_pointer(op1); node->variant.operation.operands = op1; processed = TRUE; break; #if GNU_EXTENSIONS_ALLOWED case eok_gnu_min: case eok_gnu_max: /* Transform a case like ("ab" >? "cd") by transforming both operands. Cases like that actually don't produce lvalues right now, but this code is here in case that's changed. */ op1->next = NULL; op1 = conv_array_expr_to_pointer(op1); op2 = conv_array_expr_to_pointer(op2); node->variant.operation.operands = op1; op1->next = op2; processed = TRUE; break; #endif /* GNU_EXTENSIONS_ALLOWED */ default: break; } /* switch */ } /* if */ } else if (is_constant_node(node) && in_file_scope(node)) { /* Handle the case of a compound literal array in file scope specially by representing its decayed form as a constant. That simplifies the constexpr interpreter. */ a_constant_ptr cp = node_constant(node); if (cp->is_compound_literal) { node->variant.constant.ptr = fs_constant((a_constant_repr_kind)ck_address); set_constant_address_constant(cp, node->variant.constant.ptr); processed = TRUE; } /* if */ } /* if */ if (processed) { /* The node was rewritten above. */ node->is_lvalue = node->is_xvalue = FALSE; node->type = ptr_type; } else { /* Normal case -- add an eok_array_to_pointer to do the decay. */ a_source_position *pos = &node->position; node = make_operator_node((an_expr_operator_kind)eok_array_to_pointer, ptr_type, node); node->compiler_generated = TRUE; node->position = *pos; } /* if */ return node; } /* conv_array_expr_to_pointer */ void do_array_to_pointer_conversion(an_operand *operand) /* Do array-to-pointer decay on the given operand, which is an lvalue or rvalue of array type. Don't check whether this decay is valid in the current mode -- just do it. */ { an_expr_node_ptr expr, base_expr; a_constant_ptr conaddr = local_constant(); an_operand orig_operand; a_boolean need_expr = FALSE, need_expr_for_constant = FALSE; orig_operand = *operand; expr = make_node_from_operand(operand); if (is_variable_node(expr)) { /* In some modes, in-class static data member initializer instantiations are deferred until use. If decay is needed, the initializer should be instantiated so that the resulting pointer points to a defined variable object. */ a_variable_ptr vp = node_variable(expr); if (vp->initializer_in_class && vp->is_template_variable && vp->init_kind == (an_init_kind)initk_none) { ensure_inclass_static_member_constant_initializer_is_scanned(vp); } /* if */ } /* if */ base_expr = skip_parens(expr); if (gnu_mode && !is_glvalue_node(base_expr) && is_constant_node(base_expr) && (!curr_expr_kind_is_const() || curr_expr_kind_is_traditional_const()) && node_constant(base_expr)->is_compound_literal) { /* In GNU mode, some compound literals are taken as array prvalue constants. Convert such a constant to an lvalue for a temporary containing the constant, so we can do array decay on that. Don't do this if we must produce a C++11-style constant since (a) the constant in that case could contain addresses of local variables which cannot be copied to a file-scope-allocated constant (as done by make_lvalue_operand_from_compound_constant), and (b) the expression might be folded below by a call to fold_constexpr_expr. */ make_lvalue_operand_from_compound_constant(node_constant(base_expr), operand); /* Restore the source position in case we give an error. */ restore_operand_details(operand, &orig_operand); expr = make_node_from_operand(operand); } /* if */ /* Fold to a constant address if possible and desirable. */ if (curr_expr_kind_is_traditional_const() && curr_expr_kind_is(ek_integral_constant)) { /* Array-to-pointer decay is not allowed in an integral constant expression. */ error_in_operand(ec_expr_not_integral_constant, operand); } else if (constexpr_enabled) { /* When constexpr is enabled, the expression as a whole can be interpreted later on. */ need_expr = TRUE; } else if (expr_stack->favor_constant_result && !constexpr_enabled && is_glvalue_node(expr) && constant_glvalue_address(expr, conaddr, /*address_escapes=*/TRUE)) { /* The array has a constant address, so make an address constant for the pointer. */ a_type_ptr ptr_type = type_after_array_to_pointer_transformation(expr->type); conaddr->type = ptr_type; conaddr->implicit_cast = TRUE; make_constant_operand(conaddr, operand); need_expr = curr_expr_kind_is_one_in_which_const_exprs_are_recorded(); need_expr_for_constant = need_expr; } else if (curr_expr_kind_is_evaluated_const() && !expr_stack->allow_array_decay_in_constant_expr) { /* The array-to-pointer operation must fold to a constant in a constant expression except in certain expressions such as x==x, where the result is known at compile time. */ error_in_operand(ec_expr_not_constant, operand); } else { /* Keep the result in expression form. */ need_expr = TRUE; } /* if */ if (need_expr) { /* Make an expression for the decayed pointer. Note that the call to make_node_from_operand above may already have caused the decay (e.g., if it triggered the rewrite of a captured variable). */ if (is_array_type(expr->type)) { expr = conv_array_expr_to_pointer(expr); } else { check_assertion(is_pointer_type(expr->type) || is_or_contains_error_type(expr->type)); expr->is_lvalue = FALSE; expr->is_xvalue = FALSE; } /* if */ /* Save it as either the overall result or as the backing expression for a constant result. */ if (need_expr_for_constant) { operand->variant.constant.expr = expr; } else { make_expression_operand(expr, operand); } /* if */ } /* if */ /* Restore the original source position, etc. Keep the reference entries because if the pointer to the array is used in a subscript operation or the like we would like to change the kind of reference back to modified or used instead of address-taken. But for now, set the references to address-taken because we've taken the address of the array. */ restore_operand_details_incl_ref(operand, &orig_operand); change_ref_kinds(operand->ref_entries_list, SRK_ADDRESS_TAKEN); operand->is_simple_string_literal = orig_operand.is_simple_string_literal; operand->is_id_expression = FALSE; operand->id_expression_was_parenthesized = FALSE; restore_operand_form_of_name_reference(operand, &orig_operand); release_local_constant(&conaddr); } /* do_array_to_pointer_conversion */ void conv_array_operand_to_pointer_operand(an_operand *operand) /* Apply the implicit array to pointer-to-first-element-of-array transformation to the operand. If the operand is an array lvalue it is changed to a prvalue pointer to the first element of the array. If the operand is an array rvalue, the conversion is done in some modes (C++, C99) and not in others. All other cases are left alone. */ { if (is_array_type(operand->type)) { a_boolean do_decay = FALSE; if (is_an_lvalue(operand)) { do_decay = TRUE; } else if (is_an_rvalue(operand) && (!C_mode() || c99_mode || gcc_mode || microsoft_mode)) { /* In C++ or C99 (but not in older C), an array rvalue is converted to a pointer to its first element. Make an lvalue so the conversion below will apply. GNU and Microsoft C also behave as required by C99. */ do_decay = TRUE; } /* if */ if (do_decay) { do_array_to_pointer_conversion(operand); } /* if */ } /* if */ } /* conv_array_operand_to_pointer_operand */ a_type_ptr type_after_function_to_pointer_transformation( a_type_ptr arg_type, an_operand *arg_operand) /* Determine the type of an argument of type arg_type (a function type) after the function --> pointer transformation. Return the resulting pointer type. If arg_operand is non-NULL, it points to an operand for the argument. */ { a_type_ptr ptr_type; if (arg_operand != NULL && is_sym_for_member_operand(arg_operand)) { /* Member function, so the pointer is a pointer to member. This is actually an extension -- the C++ standard doesn't allow a member function reference to decay to a pointer to member implicitly. No warning is needed here, even in strict mode; the diagnostic is issued later. */ a_symbol_ptr func_sym = arg_operand->symbol; a_symbol_ptr fund_sym = fundamental_symbol_of(func_sym); a_routine_ptr rout; check_assertion(fund_sym->kind == (a_symbol_kind)sk_member_function); rout = fund_sym->variant.routine.ptr; ptr_type = ptr_to_member_type(rout->type, parent_class_of(rout), parent_class_of(rout)); } else { /* Nonmember function. */ ptr_type = make_pointer_type(arg_type); } /* if */ return ptr_type; } /* type_after_function_to_pointer_transformation */ a_type_ptr type_of_call(an_expr_node_ptr expr) /* Return the type of the routine called by expr (the static type, which may be different from the type of the routine that is actually invoked if virtual function overriding is involved). The returned type is usually a routine type, but it can be a template parameter type or an error type. The caller must ensure that expr is a call node (parentheses must be stripped off already if necessary). */ { a_type_ptr result; check_assertion(is_call_node(expr)); result = skip_typerefs(expr->variant.operation.operands->type); if (!is_error_type(result)) { if (node_operator_is(expr, eok_dot_pm_call) || node_operator_is(expr, eok_points_to_pm_call)) { check_assertion(result->kind == (a_type_kind)tk_ptr_to_member); result = pm_member_type(result); } else if (result->kind == (a_type_kind)tk_pointer) { result = type_pointed_to(result); } else if (is_template_dependent_context()) { /* We don't know what routine is called. */ result = type_of_unknown_templ_param_nontype; } else { unexpected_condition(); } /* if */ result = skip_typerefs(result); check_assertion(result->kind == (a_type_kind)tk_routine || result->kind == (a_type_kind)tk_template_param || is_error_type(result)); } /* if */ return result; } /* type_of_call */ void conv_sym_for_member_operand_to_ptr_to_member( an_operand *operand, a_source_position *ampersand_position) /* Convert an operand for a member symbol into the corresponding pointer to member constant. If ampersand_position is non-NULL, the name was preceded by an "&" in the source, and *ampersand_position gives its position. */ { an_operand orig_operand; a_symbol_ptr member_sym, fund_sym; a_boolean has_required_ampersand; a_boolean allow_addr_of_managed_member = FALSE, force_node = FALSE; has_required_ampersand = (ampersand_position != NULL && /* Watch out for &(A::f), except in Microsoft mode. */ operand->is_id_expression && (!operand->id_expression_was_parenthesized || microsoft_bugs)); orig_operand = *operand; check_assertion(is_sym_for_member_operand(operand)); member_sym = operand->symbol; fund_sym = fundamental_symbol_of(member_sym); if (symbol_is(fund_sym, sk_member_function)) { a_routine_ptr rp = fund_sym->variant.routine.ptr; if (rp->is_consteval) { force_node = TRUE; check_address_of_consteval_function(rp); } /* if */ } /* if */ if (ampersand_position != NULL) { /* Change the start position to be used/restored to include the "&" operator. */ orig_operand.position = *ampersand_position; } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED allow_addr_of_managed_member = operand->allow_addr_of_managed_member; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Make an operand for a pointer-to-member constant. */ make_ptr_to_member_constant_operand(member_sym, member_sym, &orig_operand.position, end_position_of_operand(&orig_operand), !operand->access_control_error_reported, (a_boolean)operand->is_qualified_name, has_required_ampersand, allow_addr_of_managed_member, operand); if (force_node) { an_expr_node_ptr node = alloc_node_for_constant_operand(operand); make_expression_operand(node, operand); } /* if */ /* Restore the original source position, etc. */ restore_operand_details_incl_ref(operand, &orig_operand); /* Change the kind in the reference entries to address-taken. */ change_ref_kinds(operand->ref_entries_list, SRK_ADDRESS_TAKEN); } /* conv_sym_for_member_operand_to_ptr_to_member */ void conv_expr_function_designator_to_ptr_to_function( an_operand *operand, a_boolean will_call, a_source_position *ampersand_position) /* Convert an expression-form function designator in *operand into a prvalue for the address of the function. Note that for nonstatic member functions this produces a pointer, not a pointer to member, which is what's wanted for the function-identifying operand of a call. will_call is TRUE if the resulting expression will be used to call the function, which means (among other things) that its address will not escape. If ampersand_position is non-NULL, the function reference is preceded by an "&" operator and *ampersand_position gives its position. */ { an_expr_node_ptr expr; a_constant_ptr constant = local_constant(); an_operand orig_operand; a_boolean try_folding = FALSE; a_boolean need_expr = FALSE, need_expr_for_constant = FALSE; a_boolean template_constant = FALSE; a_routine_ptr rout; a_type_ptr rtp = NULL; check_assertion(is_expression_operand(operand) && is_a_function_designator(operand)); orig_operand = *operand; expr = make_node_from_operand(operand); check_assertion(expr->is_lvalue || is_error_node(expr)); rout = routine_from_function_expr(expr); if (rout != NULL) { rtp = skip_typerefs(rout->type); if (!will_call) { if (rtp->variant.routine.extra_info->has_enable_if_attribute) { require_true_enable_if_condition(rtp, &operand->position); } /* if */ if (rout->is_consteval) { check_address_of_consteval_function(rout); } /* if */ if (rout->has_pass_object_size_attr) { error_in_operand(ec_address_of_function_with_pass_object_size_attr, operand); goto done; } /* if */ if (operand->is_id_expression && (!operand->is_qualified_name || ampersand_position == NULL) && has_explicit_this_parameter(rtp)) { if (ampersand_position == NULL) { pos_error(ec_implicit_address_of_explicit_this_function, &operand->position); } else { pos_error(ec_address_of_unqualified_explicit_this_function, ampersand_position); } /* if */ } /* if */ } /* if */ } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (cppcli_enabled && !will_call && !operand->allow_addr_of_managed_member) { if (rout != NULL && rout->source_corresp.is_class_member && is_managed_class_type(parent_class_of(rout)) && routine_type_is_nonstatic_member_function(rtp)) { /* In C++/CLI, it's illegal to take the address of a member of a managed class (except in certain exceptional contexts). */ expr_pos_error(ec_address_of_managed_member_function, &operand->position); } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Try to fold to a constant address in a context that prefers a constant result. However, if we're going to call this function, stay in expression form because a call is inherently non-constant anyway (aside from making simpler IL, that avoids setting the address_taken flag of the function). */ try_folding = (expr_stack->favor_constant_result && !will_call); if (!try_folding && is_template_dependent_context()) { if (rout != NULL && !routine_type_is_nonstatic_member_function(rtp) && rout->source_corresp.is_class_member && parent_class_of(rout)->variant.class_struct_union.is_nonreal_class) { /* In a prototype instantiation, a static member function of the current class is template-dependent. Force its representation as a template-dependent constant. */ try_folding = TRUE; } /* if */ } /* if */ if (try_folding && constant_glvalue_address(expr, constant, /*address_escapes=*/!will_call)) { /* The address is constant and a constant is preferred in the current context. */ if (cpp11_sfinae_enabled && constant_is(constant, ck_template_param) && ampersand_position != NULL) { /* In a dependent context with an explicit "&", stay in expression form because that provides more information for rescanning. */ need_expr = TRUE; template_constant = TRUE; set_has_address_of_flag_if_needed(constant, FALSE); } else { make_constant_operand(constant, operand); need_expr = (curr_expr_kind_is_one_in_which_const_exprs_are_recorded() && !constant_is(constant, ck_template_param)); need_expr_for_constant = need_expr; } /* if */ } else if (curr_expr_kind_is_traditional_const() && !will_call) { /* A constant result is required in a constant expression. */ error_in_operand(ec_expr_not_constant, operand); } else { /* Keep the result in expression form. */ need_expr = TRUE; } /* if */ if (need_expr) { /* Make a prvalue expression from the lvalue function expression. */ if (ampersand_position != NULL) { /* Use a "&" operator to get the address because there was one in the source. */ #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; end_position = expr->expr_range.end; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ expr = make_operator_node((an_expr_operator_kind)eok_address_of, (template_constant || is_template_param_type(expr->type)) ? type_of_unknown_templ_param_nontype : make_pointer_type(expr->type), expr); set_expr_position(expr, ampersand_position, &end_position, ampersand_position); } else { /* Use implicit decay to get the address. */ expr = conv_glvalue_expr_to_prvalue(expr, (a_boolean *)NULL, (a_constant **)NULL, &operand->position); } /* if */ /* Save the expression as either the overall result or as the backing expression for a constant result. */ if (need_expr_for_constant) { /* A constant expression doesn't give you any more information than the constant itself has, so drop it. */ if (!is_constant_node(expr)) { operand->variant.constant.expr = expr; } /* if */ } else { make_expression_operand(expr, operand); if (template_constant) { make_template_param_expr_constant_operand(operand); } /* if */ } /* if */ } /* if */ done: if (ampersand_position != NULL) { /* Get the operand start position right (include the "&" operator) when we restore it from orig_operand. */ orig_operand.position = *ampersand_position; } /* if */ restore_operand_details_incl_ref(operand, &orig_operand); release_local_constant(&constant); } /* conv_expr_function_designator_to_ptr_to_function */ void conv_function_designator_to_ptr_to_function( an_operand *operand, a_source_position *ampersand_position, a_boolean allow_ctor, a_boolean will_call) /* Convert a function designator operand to a pointer to function operand. If ampersand_position is non-NULL, this is being done because of an "&" operator in the source, and *ampersand_position gives the position of that operator; otherwise, this is an implicit decay. Also handles pointers to members, including the nonstandard case of that where no explicit "&" appears. allow_ctor is TRUE if this operation is allowed if the operand is a constructor (ordinarily, taking the address of a constructor is not allowed). will_call is TRUE if the resulting operand will be used to call the function, which means (among other things) that its address will not escape. Note that a bound function lvalue for a nonstatic member function gets turned into a pointer, not a pointer to member; that's weird, but it's used in generating the function-identifying operand in a call. */ { an_operand orig_operand; a_boolean set_operand_of_ampersand = FALSE; /* If you change this routine, see also the code in type_after_function_to_pointer_transformation that does a similar transformation without generating errors. */ orig_operand = *operand; check_assertion(is_a_function_designator(operand)); /* See if there's an underlying symbol. */ if (is_sym_for_member_operand(operand) || is_indefinite_function_operand(operand)) { /* There is an underlying function symbol. */ a_symbol_ptr func_sym = operand->symbol; a_symbol_ptr fund_sym = fundamental_symbol_of(func_sym); if (fund_sym->kind == (a_symbol_kind)sk_overloaded_function) { fund_sym = fund_sym->variant.overloaded_function.symbols; } /* if */ /* Check for taking the address of a constructor or destructor, which is not allowed (ARM 12.1, 12.4). */ if (fund_sym->kind == (a_symbol_kind)sk_member_function) { a_routine_ptr rout = fund_sym->variant.routine.ptr; if ((!allow_ctor && rout->special_kind == (a_special_function_kind)sfk_constructor) || rout->special_kind == (a_special_function_kind)sfk_destructor) { expr_pos_error(ec_addr_of_constructor_or_destructor, (ampersand_position != NULL) ? ampersand_position : &operand->position); conv_to_error_operand(operand); } /* if */ } /* if */ } /* if */ if (is_error_operand(operand)) { normalize_error_operand(operand); } else if (is_indefinite_function_operand(operand)) { /* An overloaded function where we do not yet have arguments that will select a specific instance of the function. Change to a pointer to an indefinite function by changing the state to prvalue. */ /* Note that we do not check for the nonstandard "taking address of member function without using &" here; it will be checked once we know which of the functions is actually wanted. */ operand->state = (an_operand_state)os_prvalue; set_operand_of_ampersand = TRUE; } else if (operand->bound_function) { /* A bound function designator converts to a pointer, not a pointer to member. */ check_assertion(is_expression_operand(operand)); conv_expr_function_designator_to_ptr_to_function(operand, will_call, ampersand_position); } else if (is_expression_operand(operand)) { /* Convert an lvalue expression for a function to a pointer to the function. */ handle_expr: #if CHECKING /* No nonstatic member functions should come here because this form of operand is not used for them except in bound function cases, which were handled above. */ { an_expr_node_ptr expr = skip_parens(operand->variant.expression); if (is_routine_node(expr)) { a_routine_ptr rout = expr->variant.routine.ptr; a_type_ptr rout_type = skip_typerefs(rout->type); if (routine_type_is_nonstatic_member_function(rout_type)) { internal_error( "conv_function_designator_to_ptr_to_function: unexp mbr function"); } /* if */ } /* if */ } #endif /* CHECKING */ conv_expr_function_designator_to_ptr_to_function(operand, will_call, ampersand_position); } else if (is_template_param_constant_operand(operand) && tpck_is(&operand->variant.constant, tpck_expression)) { an_expr_node_ptr expr = expr_node_from_tpck_expression(&operand->variant.constant); make_expression_operand(expr, operand); operand->state = (an_operand_state)os_function_designator; goto handle_expr; } else if (is_sym_for_member_operand(operand)) { /* Convert a member name to a pointer-to-member. */ conv_sym_for_member_operand_to_ptr_to_member(operand, ampersand_position); } else { unexpected_condition(); } /* if */ /* Restore the original source position etc. Keep the reference entries because if the function is called we would like to be able to change the reference to referenced instead of address-taken. */ restore_operand_details_incl_ref(operand, &orig_operand); if (ampersand_position != NULL) { if (set_operand_of_ampersand) { /* Remember that a "&" was applied to this operand so we can create it once we make an expression. This is done late because restore_operand_details restores the is_operand_of_address_of flag. The position in the operand remains indicating the underlying operand, not including the "&". */ check_assertion(is_indefinite_function_operand(operand)); operand->is_operand_of_address_of = TRUE; operand->ampersand_position = *ampersand_position; if (operand->is_id_expression && (!operand->id_expression_was_parenthesized || microsoft_mode)) { /* Remember that the operand has the right form for a pointer to member (no parentheses around the identifier, except in Microsoft mode). */ operand->has_required_ptr_to_member_form = TRUE; } /* if */ } else { /* Change the start position of the operand to include the "&" operator that was added. */ operand->position = *ampersand_position; set_operand_expr_position_if_expr(operand, ampersand_position); } /* if */ operand->is_id_expression = FALSE; } else { /* No explicit "&". */ restore_operand_id_details(operand, &orig_operand); } /* if */ /* Change the kind in the reference entries to address-taken. */ change_ref_kinds(operand->ref_entries_list, SRK_ADDRESS_TAKEN); } /* conv_function_designator_to_ptr_to_function */ a_type_ptr do_implicit_type_transformations(a_type_ptr type, an_operand *operand) /* Do the implicit array --> pointer and function --> pointer transformations on a type. Also do the glvalue --> prvalue type transformation (dropping qualifiers as appropriate). If operand != NULL, it is the associated operand (needed for the member function --> pointer to member function transformation). */ { if (is_array_type(type)) { type = type_after_array_to_pointer_transformation(type); } else if (is_function_type(type)) { type = type_after_function_to_pointer_transformation(type, operand); } else { type = prvalue_type(type); } /* if */ return type; } /* do_implicit_type_transformations */ #if MICROSOFT_EXTENSIONS_ALLOWED a_symbol_ptr get_property_accessor_symbol(a_symbol_ptr property_sym, a_boolean put, a_boolean must_be_present, a_source_position *pos) /* property_sym points to a field or property set associated with a Microsoft property. (Both old-style __declspec properties and C++/CLI properties are handled here.) Get and return the symbol for the accessor function, either for "get" (put == FALSE) or "put"/"set" (put == TRUE). Return NULL if there is no such symbol. In that case, if must_be_present is TRUE also put out an error. The source position of the reference is given by pos. */ { a_symbol_ptr getput_sym = NULL; check_assertion(property_sym != NULL); if (symbol_is(property_sym, sk_field)) { /* And old-style property, specified via __declspec. */ a_const_char *getput_property_name; a_property_or_event_descr_ptr pedp = property_sym->variant.field.ptr ->property_or_event_descr; check_assertion(pedp != NULL); if (put) { getput_property_name = pedp->set_routine.name; } else { getput_property_name = pedp->get_routine.name; } /* if */ if (getput_property_name == NULL) { if (must_be_present) { expr_pos_error(put ? ec_no_put_property : ec_no_get_property, pos); } /* if */ } else { a_symbol_locator locator; a_type_ptr class_type; /* Look up the "get" or "put" function name in the symbol table to get the locator set. */ clear_locator(&locator, pos); (void)find_symbol(getput_property_name, (sizeof_t)strlen(getput_property_name), &locator); class_type = sym_parent_class(property_sym); /* Look for the "get" or "put" function by name in the class. */ getput_sym = class_qualified_id_lookup(&locator, class_type, IDL_NO_OPTIONS); if (getput_sym == NULL || !is_member_function_symbol(getput_sym)) { if (must_be_present) { expr_pos_st_error(put ? ec_put_property_function_missing : ec_get_property_function_missing, pos, getput_property_name); } /* if */ getput_sym = NULL; } else { /* Use a projection symbol if there is one. */ getput_sym = locator.specific_symbol; } /* if */ } /* if */ } else { /* C++/CLI property. */ check_assertion(symbol_is(property_sym, sk_property_set)); for (;;) { if (put) { getput_sym = property_sym->variant.property_info->set_accessors; } else { getput_sym = property_sym->variant.property_info->get_accessors; } /* if */ if (getput_sym != NULL) { break; } else { /* Check if perhaps one is inherited. */ a_type_ptr parent_class = sym_parent_class(property_sym); a_symbol_locator locator; a_symbol_ptr new_sym; make_locator_for_symbol(property_sym, &locator); clear_specific_symbol(locator); property_sym->is_invisible = TRUE; new_sym = class_qualified_id_lookup(&locator, parent_class, IDL_NO_OPTIONS); property_sym->is_invisible = FALSE; if (new_sym == NULL || !symbol_is(property_sym, sk_property_set)) { break; } else { property_sym = new_sym; } /* if */ } /* if */ } /* for */ if (getput_sym == NULL) { if (must_be_present && expr_error_should_be_issued()) { pos_sy_error(put ? ec_cli_set_accessor_missing : ec_cli_get_accessor_missing, pos, property_sym); } /* if */ #if CHECKING } else { a_symbol_ptr fund_sym = fundamental_symbol_of(getput_sym); check_assertion(is_member_function_symbol(fund_sym)); #endif /* CHECKING */ } /* if */ } /* if */ return getput_sym; } /* get_property_accessor_symbol */ void rewrite_property_reference( an_operand *operand, an_operand *put_operand, a_rewritten_property_reference_kind kind, a_routine_ptr *get_routine) /* *operand is an operand for a reference to a class member declared as a Microsoft property (either using __declspec(property) or the C++/CLI property syntax). Transform it to a call of an accessor routine. The access is a "put" if put_operand is non-NULL (and *put_operand gives the value to be put); the access is a "get" if put_operand is NULL. When put_operand is non-NULL, kind indicates the kind of operator being rewritten; it's used to set a kind in the expression created. If get_routine is non-NULL, *get_routine is set to a pointer to the "get" function selected, if everything goes okay, or to NULL otherwise. */ { a_symbol_ptr property_sym = operand->symbol, getput_sym; a_source_position operand_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position operand_end_position; a_source_position operand_id_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ if (get_routine != NULL) *get_routine = NULL; operand_position = operand->position; #if EXTRA_SOURCE_POSITIONS_IN_IL operand_end_position = operand->end_position; operand_id_position = operand->id_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* Get the "get" or "put" function name from the field. */ getput_sym = get_property_accessor_symbol(property_sym, /*put=*/(put_operand != NULL), /*must_be_present=*/TRUE, &operand_position); if (getput_sym == NULL) { /* Some error. */ conv_to_error_operand(operand); } else { an_operand orig_operand, function_operand, selector; an_arg_list_elem_ptr arg_list; an_expr_node_ptr argument_list; a_boolean have_selector; have_selector = (operand->variant.property_ref.object != NULL); orig_operand = *operand; if (have_selector) { /* A selector object was specified. It may or may not be used in the accessor call depending on whether a static or nonstatic property is selected by overload resolution. If it isn't bound, it will be added in via a comma operator below. */ make_expression_operand(operand->variant.property_ref.object, &selector); selector.selector_is_object_pointer = TRUE; } /* if */ /* The argument list is the subscript expression list, if any. */ arg_list = operand->variant.property_ref.subscripts; if (put_operand != NULL) { /* The last argument for a "put" is the value to be put. */ an_arg_list_elem_ptr put_arg; if (is_braced_init_list_operand(put_operand)) { put_arg = put_operand->variant.braced_init_list; put_operand->variant.braced_init_list = NULL; /* To be neat. */ } else { put_arg = alloc_arg_list_elem_for_operand(put_operand); } /* if */ if (arg_list == NULL) { arg_list = put_arg; } else { an_arg_list_elem_ptr end_arg_list = arg_list; while (!is_last_elem(end_arg_list)) { end_arg_list = next_elem(end_arg_list); } /* if */ append_elem(end_arg_list, put_arg); } /* if */ } /* if */ /* Do overload resolution to determine the function to call. */ if (!select_and_prepare_to_call_overloaded_function( getput_sym, /*is_template_id=*/FALSE, (a_template_arg_ptr*)NULL, have_selector, &selector, &arg_list, /*do_arg_dep_lookup=*/FALSE, /*use_pure_arg_dep_lookup=*/FALSE, /*use_std_for_arg_dep_lookup=*/FALSE, /*try_surrogate_functions=*/FALSE, /*is_property=*/TRUE, /*compiler_generated=*/TRUE, oc_property_access, (an_operand *)NULL, &operand_position, (a_token_sequence_number)0, (a_source_position *)NULL, (a_boolean *)NULL, &function_operand, &argument_list)) { /* Some error. */ conv_to_error_operand(operand); } else { /* Create the function call. */ an_expr_node_ptr func_call_node; a_boolean virtual_function, bound_function; /* Generally speaking, we want to restore the original operand details, but the "virtual_function" and "bound_function" flags should reflect the nature of the call. */ bound_function = function_operand.bound_function; virtual_function = function_operand.virtual_function; restore_operand_details(&function_operand, &orig_operand); function_operand.virtual_function = virtual_function && !orig_operand.is_qualified_name; function_operand.bound_function = bound_function; assemble_function_call(&function_operand, &selector, argument_list, /*compiler_generated=*/TRUE, /*arg_dep_lookup_suppressed=*/FALSE, orig_operand.is_qualified_name, /*found_through_adl=*/FALSE, /*uses_operator_syntax=*/FALSE, /*start_position=*/&null_source_position, /*operator_position=*/&operand_position, /*end_position=*/&null_source_position, operand, /*p_folded=*/(a_boolean*)NULL, &func_call_node); if (func_call_node != NULL) { an_expr_node_ptr routine_expr; an_expr_node_ptr routine_node = NULL; a_routine_ptr routine_ptr; #if !DO_IL_LOWERING a_property_or_event_descr_ptr pedp = NULL; #endif /* !DO_IL_LOWERING */ routine_expr = func_call_node->variant.operation.operands; routine_ptr = routine_and_node_from_function_expr(routine_expr, &routine_node); if (routine_node != NULL) { #if !DO_IL_LOWERING a_name_reference_ptr nrp = alloc_name_reference(); #endif /* !DO_IL_LOWERING */ routine_ptr = node_routine(routine_node); #if !DO_IL_LOWERING /* Record the information about the property in the enk_routine node to indicate that the call resulted from a transformed property reference rather than an explicit use of the property accessor. */ if (symbol_is(property_sym, sk_field)) { /* A __declspec property: Get the property description from the field. */ pedp = property_sym->variant.field.ptr->property_or_event_descr; } else { /* A C++/CLI property: Get the property description from the accessor. */ check_assertion(symbol_is(property_sym, sk_property_set)); pedp = routine_ptr->variant.property_or_event_descr; } /* if */ check_assertion(pedp != NULL); routine_node->variant.routine.name_reference = nrp; nrp->variant.property_or_event_descr = pedp; nrp->special_kind = (put_operand == NULL) ? (a_special_function_kind)sfk_property_get : (a_special_function_kind)sfk_property_set; #endif /* !DO_IL_LOWERING */ #if EXTRA_SOURCE_POSITIONS_IN_IL /* The processing above left the starting source position of the enk_routine node being that of the beginning of the object expression. Correct it to reflect the starting position of the property name. */ routine_node->expr_range.start = operand_id_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* if */ if (get_routine != NULL) { *get_routine = routine_ptr; } /* if */ if (put_operand != NULL) { func_call_node->variant.operation. rewritten_property_reference_kind = kind; } /* if */ } /* if */ #if EXTRA_SOURCE_POSITIONS_IN_IL /* The operand's end position now reflects the end of the current token, which is past the end of the field reference. Restore the original operand end position. */ operand->end_position = operand_end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* if */ /* We call free_arg_list on arg_list even though the subscripts attached to the operand are permanently allocated because we need to free the "put" operand at the end. The free is ignored for entries marked as permanently allocated. */ free_arg_list(arg_list); } /* if */ if (!is_error_operand(operand)) { if (curr_expr_kind_is_traditional_const()) { error_in_operand(ec_expr_not_constant, operand); } else if (construct_not_allowed_in_cpp11_constant_expr( ec_expr_not_constant, &operand->position)) { conv_to_error_operand(operand); } /* if */ } /* if */ rule_out_expr_kinds(ROEK_CONSTANT, operand); } /* rewrite_property_reference */ void rewrite_event_operator(an_operand *lhs, an_operand *rhs, an_operand *result, a_token_kind operator_token, a_source_position *operator_pos, a_boolean *p_err) /* An expression "lhs @= rhs" has been scanned with lhs an event reference. Produce in *result an operand rewriting that expression as a call to the event's "add" or "remove" accessor. operator_token and operator_pos describe the compound assignment operator token (only "+=" and "-=" are valid; other assignment operators trigger a diagnostic). If an error occurs, *p_err is set to TRUE and *result becomes an error operand. */ { a_boolean err = FALSE; check_assertion(is_event_ref_operand(lhs)); if (operator_token != tok_plus_assign && operator_token != tok_minus_assign) { expr_pos_error(ec_bad_event_compound_assignment, operator_pos); err = TRUE; } else { a_property_or_event_descr_ptr pedp = NULL; a_symbol_ptr event_sym = lhs->symbol, accessor_sym = NULL; a_symbol_locator accessor_loc; an_operand function_operand, selector; an_arg_list_elem_ptr arg_list = NULL; an_operand orig_operand; an_expr_node_ptr argument_list; a_boolean have_selector = (lhs->variant.event_ref.object != NULL); /* Copy the original operand before any modifications occur. */ orig_operand = *lhs; if (symbol_is(event_sym, sk_field)) { pedp = event_sym->variant.field.ptr->property_or_event_descr; } else if (symbol_is(event_sym, sk_static_data_member)) { pedp = event_sym->variant.static_data_member.variable ->property_or_event_descr; } else { unexpected_condition(); } /* if */ if (operator_token == tok_plus_assign && pedp->add_routine != NULL) { accessor_sym = symbol_for(pedp->add_routine); } else if (operator_token == tok_minus_assign && pedp->remove_routine != NULL) { accessor_sym = symbol_for(pedp->remove_routine); } else { err = TRUE; } /* if */ if (!err) { if (have_selector) { /* A selector object was specified. It may or may not be used in the accessor call depending on whether a static or nonstatic property is selected by overload resolution. If it isn't bound, it will be added in via a comma operator below. */ make_expression_operand(lhs->variant.event_ref.object, &selector); selector.selector_is_object_pointer = TRUE; } /* if */ make_locator_for_symbol(accessor_sym, &accessor_loc); accessor_loc.source_position = *operator_pos; check_ambiguity_and_verify_access(&accessor_loc); /* Turn the right-hand side into an operand list for the call. */ arg_list = alloc_arg_list_elem_for_operand(rhs); /* Do overload resolution to determine the function to call. */ if (!select_and_prepare_to_call_overloaded_function( accessor_sym, /*is_template_id=*/FALSE, (a_template_arg_ptr*)NULL, have_selector, &selector, &arg_list, /*do_arg_dep_lookup=*/FALSE, /*use_pure_arg_dep_lookup=*/FALSE, /*use_std_for_arg_dep_lookup=*/FALSE, /*try_surrogate_functions=*/FALSE, /*is_property=*/FALSE, /*compiler_generated=*/TRUE, oc_event_access, (an_operand *)NULL, operator_pos, (a_token_sequence_number)0, (a_source_position *)NULL, (a_boolean *)NULL, &function_operand, &argument_list)) { /* Some error. */ err = TRUE; } else { /* Create the function call. */ an_expr_node_ptr func_call_node; a_boolean virtual_function, bound_function; if (lhs->is_qualified_name) { /* If the left-hand operand used a qualified name to refer to the event, the accessor call is never virtual. */ function_operand.virtual_function = FALSE; } /* if */ /* Generally speaking, we want to restore the original operand details, but the "virtual_function" and "bound_function" flags should reflect the nature of the call. */ bound_function = function_operand.bound_function; virtual_function = function_operand.virtual_function; function_operand.virtual_function = virtual_function && !orig_operand.is_qualified_name; function_operand.bound_function = bound_function; assemble_function_call(&function_operand, &selector, argument_list, /*compiler_generated=*/TRUE, /*arg_dep_lookup_suppressed=*/FALSE, /*qualified_function_name=*/FALSE, /*found_through_adl=*/FALSE, /*uses_operator_syntax=*/FALSE, /*start_position=*/&null_source_position, operator_pos, /*end_position=*/&null_source_position, result, /*p_folded=*/(a_boolean*)NULL, &func_call_node); #if !DO_IL_LOWERING if (func_call_node != NULL) { an_expr_node_ptr opnd = func_call_node->variant.operation.operands; if (is_routine_node(opnd)) { /* Record the information about the event in the enk_routine node to indicate that the call resulted from a transformed event reference rather than an explicit use of the event accessor. */ a_routine_ptr rp = node_routine(opnd); a_name_reference_ptr nrp = alloc_name_reference(); opnd->variant.routine.name_reference = nrp; nrp->variant.property_or_event_descr = rp->variant.property_or_event_descr; nrp->special_kind = rp->special_kind; } /* if */ } /* if */ #endif /* !DO_IL_LOWERING */ } /* if */ } /* if */ free_arg_list(arg_list); } /* if */ if (err) { make_error_operand(result); operand_will_not_be_used_because_of_error(lhs); operand_will_not_be_used_because_of_error(rhs); *p_err = TRUE; } /* if */ } /* rewrite_event_operator */ void rewrite_event_ref_for_call(an_operand *operand, an_operand *bound_function_selector) /* operand describes an event reference for an event invocation. Replace the operand to be a member function designation for the event's "raise" accessor (or issue a diagnostic and make the operand an error operand if there is no such accessor). For non-static events, set *bound_function_selector to the selector expression used to designate the event. */ { a_property_or_event_descr_ptr pedp = NULL; a_symbol_ptr event_sym = operand->symbol; check_assertion(is_event_ref_operand(operand)); /* Retrieve the event description entry: */ if (symbol_is(event_sym, sk_field)) { pedp = event_sym->variant.field.ptr->property_or_event_descr; } else if (symbol_is(event_sym, sk_static_data_member)) { pedp = event_sym->variant.static_data_member.variable ->property_or_event_descr; } else { unexpected_condition(); } /* if */ if (pedp->raise_routine == NULL) { /* If the event has no "raise" member, it cannot be invoked. */ error_in_operand(ec_event_without_raise_invoked, operand); } else { a_symbol_ptr raise_sym = symbol_for(pedp->raise_routine); a_symbol_locator raise_loc; an_expr_node_ptr selector = operand->variant.event_ref.object; #if !DO_IL_LOWERING an_expr_node_ptr routine_node; #endif /* !DO_IL_LOWERING */ an_operand orig_operand; a_boolean virtual_function; orig_operand = *operand; make_locator_for_symbol(raise_sym, &raise_loc); raise_loc.source_position = operand->position; check_ambiguity_and_verify_access(&raise_loc); make_function_designator_operand(raise_sym, operand->is_qualified_name, /*compiler_generated=*/TRUE, &operand->position, end_position_or_null( &operand->end_position), operand->ref_entries_list, operand); /* Generally speaking, we want to restore the original operand details, but the "virtual_function" flag should reflect the kind of "raise" accessor that the call resolves to. */ virtual_function = operand->virtual_function; restore_operand_details(operand, &orig_operand); operand->virtual_function = virtual_function; #if !DO_IL_LOWERING routine_node = expr_node_from_operand(operand); if (routine_node != NULL) { /* Record the information about the event in the enk_routine node to indicate that the call resulted from a transformed event reference rather than an explicit use of the event accessor. */ a_name_reference_ptr nrp = alloc_name_reference(); routine_node->variant.routine.name_reference = nrp; nrp->variant.property_or_event_descr = pedp; nrp->special_kind = (a_special_function_kind)sfk_event_raise; } /* if */ #endif /* !DO_IL_LOWERING */ if (selector != NULL) { make_expression_operand(selector, bound_function_selector); if (pedp->is_static) { combine_unneeded_selector_with_operand( bound_function_selector, /*selector_is_object_pointer=*/TRUE, operand); } else { bind_member_function_operand_to_selector( bound_function_selector, /*selector_is_object_pointer=*/TRUE, operand); } /* if */ } /* if */ } /* if */ } /* rewrite_event_ref_for_call */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ void call_named_member_function(an_operand *selector_operand, a_const_char *member_name, a_template_arg_ptr templ_arg_list, an_arg_list_elem_ptr alep, an_operand *orig_operand, an_operand *result) /* Create in *result the representation of a call of the form expr.member(a1, a2, ...) or (if template_arg_list is non-NULL) expr.member<...>(a1, a2, ...) where expr is given by selector_operand, member is determined by member_name, the explicit template arguments (if any) are represented by templ_arg_list, and a1, a2, ... is represented by alep (possibly NULL). Operand details other than the bound_function and virtual_function flags will be copied from orig_operand to the function operand created before assembling the final call. */ { a_type_ptr class_type; a_symbol_ptr member_sym; a_symbol_locator loc; a_boolean nonreal_case = FALSE; an_expr_node_ptr arg_node_list = NULL; an_operand function_operand; class_type = skip_typerefs_not_dependent_decltypes(selector_operand->type); if (class_type->kind == (a_type_kind)tk_typeref) { /* A dependent "decltype": Use an associated proxy class. */ class_type = proxy_class_for_template_param(selector_operand->type); } else if (class_type->kind == (a_type_kind)tk_template_param) { class_type = proxy_class_for_template_param(class_type); } else if (!is_immediate_class_type(class_type)) { class_type = NULL; } /* if */ if (class_type != NULL) { clear_locator(&loc, &selector_operand->position); (void)find_symbol(member_name, (sizeof_t)strlen(member_name), &loc); member_sym = class_qualified_id_lookup(&loc, class_type, IDL_NO_OPTIONS); if (member_sym == NULL || !is_member_function_symbol(member_sym)) { if (member_sym != NULL && is_nontype_template_param_symbol(member_sym)) { nonreal_case = TRUE; arg_node_list = make_expr_list_from_argument_list( alep, /*dependent_expression=*/FALSE); make_constant_operand(member_sym->variant.constant, &function_operand); bind_member_function_operand_to_selector( selector_operand, /*selector_is_object_pointer=*/FALSE, &function_operand); } else { if (expr_error_should_be_issued()) { pos_stty_error(ec_not_a_type_member, &selector_operand->position, member_name, selector_operand->type); } /* if */ member_sym = NULL; } /* if */ } else { /* Use a projection symbol if there is one. */ member_sym = loc.specific_symbol; } /* if */ } else { if (expr_error_should_be_issued()) { pos_ty_error(ec_expr_not_class, &selector_operand->position, selector_operand->type); } /* if */ member_sym = NULL; } /* if */ if (member_sym != NULL) { /* Do overload resolution to determine the function to call. */ if (!nonreal_case && !select_and_prepare_to_call_overloaded_function( member_sym, /*is_template_id=*/templ_arg_list != NULL, &templ_arg_list, /*have_selector=*/TRUE, selector_operand, &alep, /*do_arg_dep_lookup=*/FALSE, /*use_pure_arg_dep_lookup=*/FALSE, /*use_std_for_arg_dep_lookup=*/FALSE, /*try_surrogate_functions=*/FALSE, /*is_property=*/FALSE, /*compiler_generated=*/TRUE, oc_synthesized_member_call, (an_operand *)NULL, &selector_operand->position, (a_token_sequence_number)0, (a_source_position *)NULL, (a_boolean *)NULL, &function_operand, &arg_node_list)) { /* Some error occurred. */ make_error_operand(result); } else { /* Create the function call. */ an_expr_node_ptr func_call_node; a_boolean virtual_function, bound_function; /* Generally speaking, we want to restore the original operand details, but the "virtual_function" and "bound_function" flags should reflect the nature of the call. */ bound_function = function_operand.bound_function; virtual_function = function_operand.virtual_function; if (orig_operand != NULL) { restore_operand_details(&function_operand, orig_operand); } /* if */ function_operand.virtual_function = virtual_function; function_operand.bound_function = bound_function; assemble_function_call(&function_operand, selector_operand, arg_node_list, /*compiler_generated=*/TRUE, /*arg_dep_lookup_suppressed=*/FALSE, /*qualified_function_name=*/FALSE, /*found_through_adl=*/FALSE, /*uses_operator_syntax=*/FALSE, /*start_position=*/&null_source_position, /*operator_position=*/&selector_operand->position, /*end_position=*/&null_source_position, result, /*p_folded=*/(a_boolean*)NULL, &func_call_node); if (nonreal_case && func_call_node != NULL && node_operator_is(func_call_node, eok_call)) { func_call_node->variant.operation.kind = (an_expr_operator_kind)eok_dot_member_call; } /* if */ } /* if */ } else { make_error_operand(result); } /* if */ if (!is_error_operand(result)) { if (curr_expr_kind_is_traditional_const()) { error_in_operand(ec_expr_not_constant, result); } else if (construct_not_allowed_in_cpp11_constant_expr( ec_expr_not_constant, &result->position)) { conv_to_error_operand(result); } /* if */ } /* if */ rule_out_expr_kinds(ROEK_CONSTANT, result); } /* call_named_member_function */ void call_adl_named_function(a_const_char *func_name, a_template_arg_ptr templ_arg_list, an_arg_list_elem_ptr alep, a_source_position *pos, a_token_sequence_number tok_seq_number, an_overload_context ovl_context, an_operand *result, an_expr_node_ptr *call_node) /* Create in *result the representation of a call of the form func<...>(a1, a2, ...) or func(a1, a2, ...) (if templ_arg_list is NULL) where func is determined by looking up func_name strictly through argument-dependent lookup (i.e., ordinary lookup is not done), the template argument list is represented by templ_arg_list (NULL means there is none), and a1, a2, ... is represented by alep (possibly NULL). tok_seq_number is the token sequence number from where the lookup of func_name should be done. pos is the position to use for this call. ovl_context describes the kind of construct for which this call is being resolved. This determines the diagnostic messages selected in case of errors. If call_node is non-NULL, set *call_node to the node representing the actual call. */ { a_symbol_locator loc; a_symbol_ptr sym; an_expr_stack_entry expr_stack_entry; a_boolean found_through_adl; an_operand function_operand, dummy_bound_function_selector; an_expr_node_ptr arg_nodes; clear_locator(&loc, pos); (void)find_symbol(func_name, strlen(func_name), &loc); sym = make_dummy_undefined_symbol(loc.symbol_header, &loc.source_position); push_expr_stack((an_expression_kind)ek_normal, &expr_stack_entry, /*force_object_lifetime=*/FALSE, /*suppress_object_lifetime=*/FALSE); /* Determine which function will be called. */ if (select_and_prepare_to_call_overloaded_function( sym, /*is_template_id=*/templ_arg_list != NULL, &templ_arg_list, /*have_selector=*/FALSE, (an_operand *)NULL, &alep, /*do_arg_dep_lookup=*/TRUE, /*use_pure_arg_dep_lookup=*/TRUE, /*use_std_for_arg_dep_lookup=*/TRUE, /*try_surrogate_functions=*/FALSE, /*is_property=*/FALSE, /*compiler_generated=*/TRUE, ovl_context, (an_operand *)NULL, pos, tok_seq_number, (a_source_position *)NULL, &found_through_adl, &function_operand, &arg_nodes)) { /* Generate the expression for the function call. */ #ifdef _lint /* We pass dummy_bound_function_selector rather than a null pointer constant to avoid a spurious diagnostic by Gimpel lint. */ #endif /* ifdef _lint */ assemble_function_call(&function_operand, &dummy_bound_function_selector, arg_nodes, /*compiler_generated=*/TRUE, /*arg_dep_lookup_suppressed=*/FALSE, /*is_qualified_name=*/FALSE, found_through_adl, /*uses_operator_syntax=*/FALSE, /*start_position=*/&null_source_position, /*operator_position=*/pos, /*end_position=*/&null_source_position, result, /*p_folded=*/(a_boolean*)NULL, call_node); } else { make_error_operand(result); if (call_node != NULL) *call_node = NULL; } /* if */ pop_expr_stack(); } /* call_adl_named_function */ void convert_function_template_to_single_function_full( an_operand *operand, a_boolean will_call, a_symbol_ptr *single_func_sym, a_boolean *dependent) /* If operand is a reference to a function template with explicit template arguments that reduces to a single function, change the operand to that function. See Core Issue 115. If will_call is TRUE, the resulting function will be called immediately (as opposed to, say, having its address taken). If single_func_sym is non-NULL, return the symbol for the single function in *single_func_sym, or set that to NULL if there is no single function. If dependent is non-NULL, set it to TRUE if the operand is dependent, FALSE otherwise. */ { if (single_func_sym != NULL) *single_func_sym = NULL; if (dependent != NULL) *dependent = FALSE; if (is_indefinite_function_operand(operand) && operand->is_template_id) { a_symbol_ptr orig_sym = operand->symbol; if (operand_is_dependent(operand)) { /* Don't try to resolve cases where the result is still dependent. */ conv_indefinite_function_to_unknown_dependent_function( operand, /*force_to_rvalue=*/FALSE); if (dependent != NULL) *dependent = TRUE; } else { a_template_arg_ptr new_arg_list; a_symbol_ptr base_sym; a_symbol_ptr matching_sym = NULL; an_owned_template_arg_list matching_arg_list; base_sym = fundamental_symbol_of(orig_sym); if (base_sym->kind == (a_symbol_kind)sk_function_template) { if (explicit_arg_list_identifies_specialization( base_sym, operand->template_arg_list, &new_arg_list)) { matching_sym = orig_sym; matching_arg_list = new_arg_list; } /* if */ } else if (base_sym->kind == (a_symbol_kind)sk_overloaded_function) { /* An overload set possibly containing function templates. */ a_symbol_ptr proj_sym; an_overload_set_traversal_block ostblock; for (proj_sym = set_up_overload_set_traversal_simple(base_sym, &ostblock); proj_sym != NULL; proj_sym = next_symbol_in_overload_set(&ostblock)) { base_sym = fundamental_symbol_of(proj_sym); if (base_sym->kind == (a_symbol_kind)sk_function_template) { if (explicit_arg_list_identifies_specialization( base_sym, operand->template_arg_list, &new_arg_list)) { if (matching_sym != NULL) { /* There's more than one matching function template, so leave the operand as it is. */ matching_sym = NULL; matching_arg_list = NULL; break; } else { matching_sym = proj_sym; matching_arg_list = new_arg_list; } /* if */ } /* if */ } /* if */ } /* for */ } /* if */ if (matching_sym != NULL) { /* The template reference -- something like f<1> -- corresponds to a single function. */ an_operand orig_operand = *operand; a_symbol_ptr sym; a_source_position *ampersand_pos = NULL; if (orig_operand.is_operand_of_address_of) { ampersand_pos = &orig_operand.ampersand_position; } /* if */ a_template_arg_ptr orig_arg_list = matching_arg_list.release(); sym = find_template_function(matching_sym, &orig_arg_list, /*explicit_arg_list_present=*/TRUE, &orig_operand.position); if (orig_operand.name_reference_set) { orig_operand.name_reference.orig_template_arg_list = orig_arg_list; } else { free_template_arg_list(orig_arg_list); } /* if */ check_assertion(sym != NULL && is_simple_function_symbol(sym)); if (single_func_sym != NULL) *single_func_sym = sym; if (sym->kind == (a_symbol_kind)sk_member_function && routine_type_is_nonstatic_member_function( sym->variant.routine.ptr->type)) { /* A nonstatic member function. */ make_sym_for_member_operand(sym, (a_boolean)orig_operand.is_qualified_name, orig_operand.ref_entries_list, operand); restore_operand_details(operand, &orig_operand); if (orig_operand.has_required_ptr_to_member_form) { operand->is_id_expression = TRUE; } /* if */ if (is_a_prvalue(&orig_operand)) { conv_sym_for_member_operand_to_ptr_to_member(operand, ampersand_pos); } /* if */ /* Restore any name reference. */ if (orig_operand.name_reference_set) { operand->name_reference_set = TRUE; operand->name_reference = orig_operand.name_reference; } /* if */ } else { /* A nonmember function or static member function. */ make_function_designator_operand(sym, (a_boolean) orig_operand.is_qualified_name, /*compiler_generated=*/FALSE, &orig_operand.position, end_position_of_operand( &orig_operand), orig_operand.ref_entries_list, operand); /* Restore any name reference. */ if (orig_operand.name_reference_set) { operand->name_reference_set = TRUE; operand->name_reference = orig_operand.name_reference; } /* if */ if (is_a_prvalue(&orig_operand)) { conv_function_designator_to_ptr_to_function(operand, ampersand_pos, /*allow_ctor=*/FALSE, will_call); } /* if */ restore_operand_details(operand, &orig_operand); restore_operand_id_details(operand, &orig_operand); } /* if */ } /* if */ } /* if */ } /* if */ } /* convert_function_template_to_single_function_full */ void convert_function_template_to_single_function_if_possible( an_operand *operand, a_boolean will_call) /* If operand is a reference to a function template with explicit template arguments that reduces to a single function, change the operand to that function. See Core Issue 115. If will_call is TRUE, the resulting function will be called immediately (as opposed to, say, having its address taken). */ { convert_function_template_to_single_function_full(operand, will_call, (a_symbol_ptr *)NULL, (a_boolean *)NULL); } /* convert_function_template_to_single_function_if_possible */ a_boolean conv_bound_function_to_static_selection( an_operand *operand, an_operand *bound_function_selector) /* operand is a bound function, with bound_function_selector the associated selector. If it is a reference to a function template with explicit arguments such that a single instance of the template is specified, and the instance is static, change the operand to an un-bound static selection for the specific function. */ { a_boolean converted = FALSE, dependent = FALSE; a_symbol_ptr single_func_sym; check_assertion(operand->bound_function); convert_function_template_to_single_function_full(operand, /*will_call=*/FALSE, &single_func_sym, &dependent); if (single_func_sym != NULL) { a_symbol_ptr sym = fundamental_symbol_of(single_func_sym); if (sym->kind == (a_symbol_kind)sk_member_function && !routine_type_is_nonstatic_member_function( sym->variant.routine.ptr->type)) { /* A single function has been identified, and it is a static member function, so we can convert the bound function to a static selection. */ converted = TRUE; } /* if */ } else if (dependent) { /* If the construct is dependent, we cannot reliably tell whether a static selection is possible until a real instantiation is performed. Proceed as if a static selection will be possible. */ converted = TRUE; } /* if */ if (converted) { operand->bound_function = FALSE; combine_unneeded_selector_with_operand(bound_function_selector, (a_boolean)bound_function_selector ->selector_is_object_pointer, operand); } /* if */ return converted; } /* conv_bound_function_to_static_selection */ void error_if_indefinite_function(an_operand *operand) /* If the given operand is an indefinite function, issue an error and change the operand to an error operand. */ { if (is_indefinite_function_operand(operand)) { sym_error_in_operand(ec_indeterminate_overloaded_function, operand, operand->symbol); } /* if */ } /* error_if_indefinite_function */ void do_operand_transformations(an_operand *operand, a_transformation_options_set options) /* Do some implicit operand transformations on the indicated operand. The transformations are: (1) Conversion of a function designator to a pointer-to-function. (2) Conversion of an array glvalue to pointer-to-first-element. (3) (Not really a transformation, but...) Checking for indefinite functions. (4) Conversion of a glvalue to a prvalue. (5) Conversion of a reference to a member declared as a Microsoft property to a call of the appropriate "get" function, and similarly for event references. (6) (Also not really a transformation...) Checking for braced-init-lists not handled explicitly elsewhere. The flags in options can be used to suppress one or more of these transformations. */ { a_boolean will_call = (options & TOPT_WILL_CALL) != 0; #if MICROSOFT_EXTENSIONS_ALLOWED if (microsoft_mode) { if (is_property_ref_operand(operand)) { if (!(options & TOPT_SUPPRESS_RVALUE_PROPERTY_REWRITE)) { /* This operand is a reference to a member declared as a Microsoft property. Change it to a call of the appropriate "get" function. */ rewrite_property_reference(operand, (an_operand *)NULL, (a_rewritten_property_reference_kind)rprk_none, (a_routine_ptr *)NULL); } /* if */ } else if (is_event_ref_operand(operand)) { /* Events cannot be accessed as rvalues. */ error_in_operand(ec_invalid_event_use, operand); } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (is_array_type(operand->type)) { /* An array lvalue or rvalue. */ if (!(options & TOPT_SUPPRESS_ARRAY_TO_POINTER_CONVERSION)) { /* In most contexts, an operand of array type is changed to "pointer to first element of array". */ conv_array_operand_to_pointer_operand(operand); } /* if */ } else if (is_a_glvalue(operand)) { /* A non-array glvalue. */ if (!(options & TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION)) { /* Convert a glvalue to a prvalue. */ if ((options & TOPT_COPY_CLASS_ON_CONV_TO_RVALUE) && !C_mode() && is_class_struct_union_type(operand->type) && !is_incomplete_type(operand->type) /* For error recovery. */ #if MICROSOFT_EXTENSIONS_ALLOWED && !is_managed_class_type(operand->type) #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ ) { /* When converting a class object to a prvalue in C++ mode, make a copy. This is required by the standard, but it only applies in some unusual situations, so it's not the default. */ temp_init_from_operand(operand, /*result_is_lvalue=*/FALSE); } else { conv_glvalue_to_prvalue(operand); } /* if */ } /* if */ } /* if */ if (is_indefinite_function_operand(operand) && !(options & TOPT_SUPPRESS_CHECK_FOR_INDEFINITE_FUNCTION)) { /* Issue an error for an indefinite function (i.e., a C++ overloaded function that wasn't called, so we were never able to determine which function was intended). */ /* Try to convert a template with explicit arguments to a single function. */ convert_function_template_to_single_function_if_possible(operand, will_call); error_if_indefinite_function(operand); } /* if */ if (is_a_function_designator(operand)) { if (is_sym_for_member_operand(operand) ? (options & TOPT_SUPPRESS_MEMBER_FUNC_TO_PM_CONVERSION) : (options & TOPT_SUPPRESS_FUNCTION_TO_POINTER_CONVERSION)) { /* The applicable pointer-to-function conversion is suppressed. */ } else { /* In most contexts, an entity of type "function returning type" is changed to "pointer to function returning type". See section 3.2.2.1 in the ANSI C standard. Also, member function to pointer to member (not a standard conversion, but allowed as an accommodation to existing practice). */ /* In Microsoft mode, allow an explicit call of a constructor, e.g., "p->X::X()". */ a_boolean allow_ctor = (will_call && ms_extensions); conv_function_designator_to_ptr_to_function(operand, (a_source_position *)NULL, allow_ctor, will_call); } /* if */ } else if (is_braced_init_list_operand(operand)) { /* Issue an error for a braced-init-list not handled explicitly. There is no option to suppress this. */ check_arg_list_elem_is_expression(operand->variant.braced_init_list); conv_to_error_operand(operand); } /* if */ } /* do_operand_transformations */ static a_boolean expr_gets_volatile_lvalue_to_rvalue_conv( an_expr_node_ptr expr) /* Return TRUE if the expression expr has one of the forms described in [expr]p11 for discarded-value expressions that get the glvalue-to-prvalue conversion for a volatile glvalue. */ { a_boolean do_conv = FALSE; expr = skip_parens(expr); if (is_variable_node(expr)) { /* id-expression. */ do_conv = TRUE; } else if (is_operation_node(expr)) { an_expr_operator_kind op = expr->variant.operation.kind; an_expr_node_ptr op1 = expr->variant.operation.operands; switch (op) { case eok_subscript: case eok_dot_field: case eok_points_to_field: case eok_indirect: case eok_pm_field: case eok_pm_points_to_field: do_conv = TRUE; break; case eok_question: case eok_vector_question: /* "?" gets the conversion if both the 2nd and 3rd operands do. */ do_conv = (expr_gets_volatile_lvalue_to_rvalue_conv(op1->next) && expr_gets_volatile_lvalue_to_rvalue_conv(op1->next->next)); break; case eok_comma: /* "," gets the conversion if the 2nd operand does. */ do_conv = expr_gets_volatile_lvalue_to_rvalue_conv(op1->next); break; default: break; } /* switch */ } /* if */ return do_conv; } /* expr_gets_volatile_lvalue_to_rvalue_conv */ void do_void_operand_transformations(an_operand *operand, a_boolean force_lvalue_to_rvalue) /* Do whatever transformations are appropriate on a void expression operand, e.g., lvalue-to-rvalue in C, not in C++. If force_lvalue_to_rvalue is TRUE, the lvalue-to-rvalue (etc.) transformations are forced even in C++ mode. */ { a_transformation_options_set options = TOPT_NO_OPTIONS; if (!C_mode() && !force_lvalue_to_rvalue) { /* In C++, lvalue-to-rvalue transformations are not done on an expression scanned as a void expression. */ options = TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION; if ((cpp11_mode || gpp_mode) && is_volatile_qualified_type(operand->type)) { /* C++11 requires an lvalue-to-rvalue conversion on lvalues with a volatile type that have a certain form. g++ seems to have done this (or just the C semantics) all along. */ if (is_expression_operand(operand) && expr_gets_volatile_lvalue_to_rvalue_conv( operand->variant.expression)) { /* Do not suppress the lvalue-to-rvalue conversion. */ options = TOPT_NO_OPTIONS; } /* if */ } /* if */ /* The array-to-pointer and function-to-pointer conversions are suppressed in all cases in C++ mode. */ options |= (TOPT_SUPPRESS_ARRAY_TO_POINTER_CONVERSION | TOPT_SUPPRESS_FUNCTION_TO_POINTER_CONVERSION); } /* if */ do_operand_transformations(operand, options); } /* do_void_operand_transformations */ void eliminate_unusual_operand_kinds(an_operand *operand) /* Eliminate unusual operand kinds, e.g., indefinite functions and symbol-for-member operands, possibly by giving errors for them. This can be used to guarantee that going forward the operand will have one of the "normal" kinds. */ { do_operand_transformations(operand, (TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION | TOPT_SUPPRESS_ARRAY_TO_POINTER_CONVERSION | TOPT_SUPPRESS_FUNCTION_TO_POINTER_CONVERSION)); } /* eliminate_unusual_operand_kinds */ a_boolean still_an_lvalue(a_type_ptr type_before_cast, a_type_ptr type_cast_to) /* In pcc, SVR4, and Microsoft C modes certain lvalues when cast remain as lvalues after the cast. Return TRUE if a cast with the before/after types given should leave its result still an lvalue. This routine is called only in C mode. */ { a_boolean is_still_an_lvalue = FALSE; /* Be sure to retrieve the alignment before skipping typerefs in case this is ever called in GNU mode (where attributes can change the alignment of typedef types). */ a_targ_alignment alignment_before_cast = alignment_of_type(type_before_cast), alignment_after_cast = alignment_of_type(type_cast_to); type_before_cast = skip_typerefs(type_before_cast); type_cast_to = skip_typerefs(type_cast_to); check_assertion(C_mode()); if (!is_scalar_type(type_before_cast) || !is_scalar_type(type_cast_to)) { /* Rule out casts from/to structs. */ /* is_still_an_lvalue = FALSE; -- already set. */ } else if (identical_types(type_cast_to, type_before_cast)) { /* Same type, operand stays an lvalue. */ is_still_an_lvalue = TRUE; } else if (!ms_extensions && (is_floating_type(type_before_cast) || is_floating_type(type_cast_to))) { /* The source or destination types are floating types, so there's actual conversion involved. */ /* is_still_an_lvalue = FALSE; -- already set. */ } else if (is_bool_type(type_cast_to)) { /* Conversion between other types and bool maps zero/non-zero to false/true, so it's a real conversion. */ /* is_still_an_lvalue = FALSE; -- already set. */ } else if (type_cast_to->size == type_before_cast->size && alignment_after_cast == alignment_before_cast) { /* The types are not floating or bool types, and they have the same size and alignment. */ is_still_an_lvalue = TRUE; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (ms_extensions && (is_integral_or_enum_type(type_before_cast) || is_pointer_type(type_before_cast)) && is_integral_or_enum_type(type_cast_to)) { /* In Microsoft C mode lvalue casts involving integral types of different sizes are allowed -- e.g., long l; ++(char)l; // affects only the low-order 8 bits Also casts of pointer types to integral type. */ is_still_an_lvalue = TRUE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ return is_still_an_lvalue; } /* still_an_lvalue */ static void record_local_auto_object( an_expr_or_stmt_traversal_block_ptr tblock) /* Called from examine_expr_for_auto_object when the addressing path leads to a local variable or parameter. Normally that ends the traversal. When tblock->look_for_temporaries is TRUE, keep going, so that a temporary on another part of the path (e.g., the other operand of a "?" operator) is still found and reported instead. */ { if (!tblock->is_temp) { tblock->result = TRUE; tblock->is_temp = FALSE; if (!tblock->look_for_temporaries) tblock->terminate = TRUE; } /* if */ } /* record_local_auto_object */ static void examine_expr_for_auto_object( 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 its underlying object is auto. 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. */ { if (is_glvalue_node(expr) || (tblock->follow_class_rvalue_addressing_path && is_class_struct_union_type(expr->type)) || (tblock->look_for_temporaries && is_array_type(expr->type))) { /* The expression passed in is a glvalue for an object (or a class prvalue object, or, when looking for temporaries, an array prvalue). */ /* Skip some operation nodes (like value casts) that do not affect the underlying object. */ while (is_operation_node(expr)) { if (node_operator_is(expr, eok_lvalue_cast) || node_operator_is(expr, eok_lvalue_adjust)) { expr = expr->variant.operation.operands; } else if (node_operator_is(expr, eok_ref_indirect)) { an_expr_node_ptr opnd = expr->variant.operation.operands; if (is_operation_node(opnd) && node_operator_is(opnd, eok_reference_to)) { /* An eok_ref_indirect+eok_reference_to pair is sometimes used to turn a temporary into an xvalue. */ expr = opnd->variant.operation.operands; } else { break; } /* if */ } else { break; } /* if */ } /* while */ if (is_variable_node(expr)) { a_variable_ptr var = node_variable(expr); if (!var_has_static_or_thread_storage_duration(var)) { /* An lvalue for a nonstatic local variable or a parameter. If the variable is not in the current function (i.e., it is a variable from a scope enclosing a lambda), do not consider it local in this context. */ if (var->source_corresp.enclosing_routine == curr_routine_or_null()) { record_local_auto_object(tblock); } /* if */ } /* if */ } else if (node_is(expr, enk_param_ref)) { /* An lvalue for a parameter outside an associated function definition. An enk_param_ref for "this" won't get here because it's not an lvalue, but the answer would be right even if it did. */ record_local_auto_object(tblock); } else if (node_is(expr, enk_temp_init)) { if (!expr->variant.init.dynamic_init->static_temp) { /* An lvalue for a nonstatic temporary. */ tblock->result = TRUE; tblock->is_temp = TRUE; tblock->terminate = TRUE; } /* if */ } else if (tblock->look_for_temporaries && !is_glvalue_node(expr)) { /* A class or array prvalue (e.g., a function call) on the addressing path, for example the object expression of "f().x": a temporary is materialized for it. */ tblock->result = TRUE; tblock->is_temp = TRUE; tblock->terminate = TRUE; } else if (tblock->look_for_temporaries && is_operation_node(expr) && node_operator_is(expr, eok_indirect)) { /* A reference bound to an array prvalue (e.g., "A{1, 2}" with "A" an array type) is represented as "*" applied to the decayed array temporary (under an eok_lvalue_adjust, skipped above). */ an_expr_node_ptr ptr = expr->variant.operation.operands; if (is_operation_node(ptr) && node_operator_is(ptr, eok_array_to_pointer)) { an_expr_node_ptr array = ptr->variant.operation.operands; if (node_is(array, enk_temp_init) && !array->variant.init.dynamic_init->static_temp) { tblock->result = TRUE; tblock->is_temp = TRUE; tblock->terminate = TRUE; } /* if */ } /* if */ } /* if */ } else { /* The expression passed in is a pointer to an object. */ if (is_operation_node(expr) && node_operator_is(expr, eok_cast) && is_pointer_type(expr->type)) { /* Allow a cast that passes through a pointer. This test is a little broader than the one done in the traversal routine. */ an_expr_node_ptr operand = expr->variant.operation.operands; if (is_pointer_type(operand->type)) { traverse_expr(operand, tblock); tblock->suppress_subtree_walk = TRUE; } /* if */ } /* if */ } /* if */ } /* examine_expr_for_auto_object */ static a_boolean glvalue_for_auto_object(an_expr_node_ptr expr, a_boolean follow_prvalues, a_boolean *is_temp) /* Do the work for is_glvalue_for_auto_object and is_glvalue_for_auto_object_or_temp_subobject. If follow_prvalues is TRUE, the traversal continues through class prvalues, so that a subobject of a temporary (e.g., "S().x") is found. */ { a_boolean is_auto_object = FALSE; if (is_temp != NULL) *is_temp = FALSE; if (is_glvalue_node(expr)) { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = examine_expr_for_auto_object; tblock.follow_addressing_path = TRUE; tblock.follow_class_rvalue_addressing_path = follow_prvalues; tblock.look_for_temporaries = follow_prvalues; traverse_expr(expr, &tblock); is_auto_object = tblock.result; if (is_temp != NULL) *is_temp = tblock.is_temp; } /* if */ return is_auto_object; } /* glvalue_for_auto_object */ a_boolean is_glvalue_for_auto_object(an_expr_node_ptr expr, a_boolean *is_temp) /* Return TRUE if expr is a glvalue whose underlying object is known to be an automatic (stack-based) entity. The safe answer is FALSE. If the underlying entity is a temporary, return *is_temp set to TRUE. is_temp can be NULL if that information is not needed. */ { return glvalue_for_auto_object(expr, /*follow_prvalues=*/FALSE, is_temp); } /* is_glvalue_for_auto_object */ a_boolean is_glvalue_for_auto_object_or_temp_subobject( an_expr_node_ptr expr, a_boolean *is_temp) /* Like is_glvalue_for_auto_object, but also return TRUE (with *is_temp set to TRUE) if expr is a glvalue for a subobject of a class prvalue temporary, e.g., "S().x". */ { return glvalue_for_auto_object(expr, /*follow_prvalues=*/TRUE, is_temp); } /* is_glvalue_for_auto_object_or_temp_subobject */ a_boolean is_prvalue_for_auto_object(an_expr_node_ptr expr, a_boolean *is_temp) /* Return TRUE if expr is a prvalue whose underlying object is known to be an automatic (stack-based) entity. This is possible only for class prvalues. The safe answer is FALSE. If the underlying entity is a temporary, return *is_temp set to TRUE. */ { a_boolean is_auto_object = FALSE; *is_temp = FALSE; if (!is_glvalue_node(expr) && is_class_struct_union_type(expr->type)) { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = examine_expr_for_auto_object; tblock.follow_addressing_path = TRUE; tblock.follow_class_rvalue_addressing_path = TRUE; traverse_expr(expr, &tblock); is_auto_object = tblock.result; *is_temp = tblock.is_temp; } /* if */ return is_auto_object; } /* is_prvalue_for_auto_object */ a_boolean is_address_of_auto_object(an_expr_node_ptr expr, a_boolean *is_temp) /* Return TRUE if expr is a prvalue address of an object that is known to be an automatic (stack-based) entity. The safe answer is FALSE. If the underlying entity is a temporary, return *is_temp set to TRUE. */ { a_boolean is_addr_of_auto = FALSE; *is_temp = FALSE; if (!is_glvalue_node(expr) && is_pointer_type(expr->type)) { an_expr_or_stmt_traversal_block tblock; clear_expr_or_stmt_traversal_block(&tblock); tblock.process_expr = examine_expr_for_auto_object; tblock.follow_addressing_path = TRUE; /* A pointer into a prvalue.field xvalue (CWG 616) addresses a subobject of the class prvalue, which is a temporary even when it is not represented by an explicit temporary (e.g., for a trivially destructible class). */ tblock.follow_class_rvalue_addressing_path = selection_from_prvalue_is_xvalue; tblock.look_for_temporaries = selection_from_prvalue_is_xvalue; traverse_expr(expr, &tblock); is_addr_of_auto = tblock.result; *is_temp = tblock.is_temp; } /* if */ return is_addr_of_auto; } /* is_address_of_auto_object */ a_type_ptr boolean_result_type(void) /* Return the result type for a boolean expression (one that returns 0 or 1 in C, and false or true in C++). */ { a_type_ptr result_type; if (bool_is_keyword) { /* bool exists (C++), so the result type is bool. */ result_type = bool_type(); } else if (expr_stack != NULL && curr_expr_kind_is(ek_pp)) { /* All integers have a type of long in the preprocessor. In C99 mode, they have type intmax_t, which might be long long. */ result_type = integer_type((an_integer_kind) (c99_mode ? targ_intmax_kind : (an_integer_kind)ik_long)); } else { /* Result type is int. */ result_type = integer_type((an_integer_kind)ik_int); } /* if */ return result_type; } /* boolean_result_type */ a_boolean check_boolean_controlling_expr(an_operand *operand) /* Do some checks on a boolean controlling expression (e.g., "i != 0" in "(i != 0) ? j : k"). Check that (if bool is enabled) it has bool type or can be converted to it, or (if bool is disabled) it's a scalar (arithmetic or pointer) or a pointer to member; return FALSE if not. Also, convert the expression to bool if bool is enabled. This routine does not attempt conversions from class types to built-in types to get a boolean expression (see process_boolean_controlling_expression). That's convenient for operands of potentially-overloaded operators, because the search for conversions to the right operand types is handled by check_for_operator_overloading. In modern C++ modes, this routine implements "contextually converted to bool" (C++11 [conv]p4) except for user-defined conversions (see also process_boolean_controlling_expression). */ { a_boolean okay = FALSE; an_expr_node_ptr expr; an_expr_operator_kind op; an_expr_node_ptr operand1; an_operand orig_operand; a_boolean constant_pointer_case = FALSE; /* Check for possible misuse of "=" where "==" was intended. */ if (is_expression_operand(operand)) { expr = skip_parens(operand->variant.expression); if (is_operation_node(expr)) { op = expr->variant.operation.kind; operand1 = expr->variant.operation.operands; if (is_simple_assignment(op)) { /* An assignment operator at the top level. Check for "x = constant", which was probably intended to be "x == constant". */ if (is_constant_node(operand1->next)) { expr_pos_warning(ec_assign_where_compare_meant, &operand->position); } /* if */ } /* if */ } /* if */ } /* if */ if (!curr_expr_kind_is_const()) { /* Force a constant addressing expression to a constant (not needed in constant-expression contexts since those have to be constant-evaluated at the top anyway). */ force_operand_to_constant_if_possible(operand); } /* if */ orig_operand = *operand; /* Remember whether or not the expression is a constant pointer or pointer-to-member, before any (other) changes are made. */ /* When constexpr is enabled, constants can come from all kinds of things, e.g., calls, so turn off the warning. */ if (!constexpr_enabled && is_a_prvalue(operand) && is_constant_operand(operand) && (is_pointer_type(operand->type) || is_ptr_to_member_type(operand->type))) { constant_pointer_case = TRUE; #if GNU_EXTENSIONS_ALLOWED { a_constant_ptr con = &operand->variant.constant; if (con->kind == (a_constant_repr_kind)ck_address && ((con->variant.address.kind == (an_address_base_kind)abk_routine && con->variant.address.variant.routine->is_weak) || (con->variant.address.kind == (an_address_base_kind)abk_variable && con->variant.address.variant.variable->is_weak))) { /* No warning for GNU weak externals. */ constant_pointer_case = FALSE; } /* if */ } #endif /* GNU_EXTENSIONS_ALLOWED */ } /* if */ if (bool_is_keyword) { /* bool is enabled. The expression must have bool type or be convertible to bool. */ if (is_bool_type(operand->type)) { okay = TRUE; } else { /* The expression must be convertible to bool. */ a_std_conv_descr std_conv; if (impl_conversion_possible(operand->type, is_constant_operand(operand), (a_boolean)operand-> is_simple_string_literal, operand_is_function(operand), /*is_copy_initialization=*/FALSE, &operand->variant.constant, bool_type(), /*singleton_braced_init=*/FALSE, /*allow_qualifier_or_eh_mismatch=*/FALSE, /*suppress_extensions=*/FALSE, ec_expr_not_bool, &std_conv) #if MICROSOFT_EXTENSIONS_ALLOWED /* C++/CLI handles are not actually convertible to bool, but they can be tested in boolean controlling expressions. */ || (cli_or_cx_enabled && is_handle_type(operand->type)) #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ ) { okay = TRUE; /* Convert the expression to bool. */ cast_operand(bool_type(), operand, /*is_implicit_cast=*/TRUE); } else { error_in_operand(ec_expr_not_bool, operand); } /* if */ } /* if */ } else { /* bool is disabled. The expression must have scalar or pointer to member type. */ if (is_ptr_to_member_type(operand->type)) { /* Pointer to member type is okay. */ okay = TRUE; } else if (is_template_param_type(operand->type)) { /* Template parameter types are okay. */ okay = TRUE; } else { /* Check that the operand is a scalar. */ okay = check_scalar_operand(operand); } /* if */ } /* if */ if (okay) { /* Issue a warning if the expression is constant and of pointer or pointer-to-member type. */ /* This test formerly checked other things, but it appears there's no good way of diagnosing any use of a constant arithmetic value here. Even something like "if (1) ..." might have come from a macro, which would be reasonable coding. */ if (constant_pointer_case) { expr_pos_warning(ec_boolean_controlling_expr_is_constant, &orig_operand.position); } /* if */ } /* if */ /* Restore the original source position. */ restore_operand_details(operand, &orig_operand); return okay; } /* check_boolean_controlling_expr */ /* Concepts, constraints, and subsumption ====================================== C++20 introduces a notion of "constrained templates", including a "concept" abstraction of constraints (predicated on template arguments) and a mechanism to compare constraints called "subsumption". Constraints ----------- Constraints are predicate (boolean) expressions in which three operations are treated specially if they appear "at the top level": - concept-ids - logical AND (&&, called "conjunction") - logical OR (||, called "disjunction") "At the top level", here, means that no other operation appears on top of them (parentheses are okay). For example, with: template concept C = sizeof(T) < 100 && requires (T p) { *p; }; a constraint of the form "alignof(T) < 10 && C" would have two top-level "conjunctions" and a top-level concept-id (a conjunction inside a top-level concept-id is also "top-level"). In contrast, a constraint of the form "f(C, sizeof(T))" would have no special top-level operations because the concept-id C is under an operation that isn't one of the special three (a call operator in this case). An expression that is an operand of one of the special three but not a special operation itself is called an "atomic constraint". Constraint comparison and subsumption ------------------------------------- Sometimes we need to check if a declaration is "more constrained" than another. A key relation in that context is "subsumes", which essentially means "is at least as constrained as". Let "SS" denotes "subsumes" and "!SS" denote "does not subsume", then clearly for all constraints X, Y, and Z the following hold: X SS X (X AND Y) SS X X !SS (X AND Y) X SS (X OR Y) (X OR Y) !SS X To determine subsumption we therefore need special rules to handle AND on the right hand side and OR on the left hand side. These rules are: (X SS (Y AND Z)) <=> (X SS Y) AND (X SS Z) // Rule 1 ((X OR Y) SS Z) <=> (X SS Z) AND (Y SS Z) // Rule 2 The first rule expresses that a constraint X is at least as constrained as the conjunction of two constraints X and Y, if it is at least as constrained as each one separately (which matches intuition). The second rule is similarly intuitive: For a disjunction of two constraints X and Y to be at least as constrained as a constraint Z, each alternative (X and Y) has to be as constrained as Z. Consider constraints E1 and E2 expressed through conjunctions (AND) and disjunctions (OR) of atomic constraints. Let E1[k] be the set of all constraints obtained by considering all combinations of disjunctions (OR) in E1. E.g., if E1 = (X OR Y) AND (Y OR Z) then we produce E1[1] = X AND Y E1[2] = X AND Z E1[3] = Y AND Y E1[4] = Y AND Z Note that E1 is equivalent to E1[1] OR E1[2] OR E1[3] OR ... and thus by Rule 2 above E1 subsumes E2 if and only if each of the E1[k] subsumes E2. Let E2[k] be the set of all constraints obtained by considering all combinations of conjunctions (AND) in E2. E.g., if E2 = (P AND Q) OR (P AND R) we produce E2[1] = P OR P E2[2] = P OR R E2[3] = Q OR P E2[4] = Q OR R E2 is then equivalent to E2[1] AND E2[2] AND E2[3] AND ... and thus by Rule 1 E1 subsumes E2 if E1 subsumes every E2[k]. This leads to the following algorithm for "E1 SS E2": result = TRUE for each E1[i] for each E2[j] if not (E1[i] SS E2[j]) result = FALSE exit Furthermore, because E1[k] is a conjunction (all ANDs applied to atomic constraints) and E2[k] is a disjunction (ORs applied to atomic constraints), "E1[i] SS E2[j]" amounts to checking that one term in E1[i] is identical to one term in E2[j] (that follows from the rules "(X AND Y) SS X" and "X SS (X OR Y)" above). The C++20 standard expresses this in N4849 [temp.constr.order] in terms of disjunctive normal form of a constraint (whose terms correspond to the E1[k]) and the conjunctive normal form of another constraint (whose terms correspond to the E2[k]). Implementation: Constraint charts --------------------------------- We generate the E1[k] or E2[k] by creating a flattened representation of the constraint tree(s). The data structure that encapsulates this is a "constraint chart" (a_constraint_chart), whose main component is an array (Dyn_array) of compact elements that represent one of four possibilities: 1) an atomic constraint 2) a concept use 3) a conjunction 4) a disjunction The conjunction and disjunction cases contain a flag that determines whether their first or second operand is "active". Each possible E1[k] is generated by "counting through" (0000..., 1000..., 0100..., 1100, 0010..., etc.) the flags of the disjunctions (process_disjunctive_clause). Similarly, the E2[k] sequences are generated by "counting through" the flags of the conjunctions (process_conjunctive_clause). process_disjunctive_clause maps (parameter expr_map) the active atomic constraint nodes to the chart indices for the current disjunctive clause. That allows process_conjunctive_clause to quickly locate "terms" of E2[k] that are present in E1[k] (a condition for subsumption). When terms with matching constraint expressions are found, they are recorded in a list for potential later checking of the template parameter mapping. Since that check is expensive, it is postponed until we know that subsumption has not been disproved based on other criteria. */ #define CK_ATOMIC 0 #define CK_CONCEPT 1 #define CK_AND 2 #define CK_OR 3 struct a_charted_constraint { uint32_t kind:2; /* The kind of constraint node this represents. */ uint32_t link:30; /* For CK_ATOMIC and CK_CONCEPT entries, the 1-based index of the "parent concept" (or 0 if none). That is used to compare mappings. For CK_AND and CK_OR entries, the index of the second constraint operand. */ uint32_t parent_op; /* The 1-based index of the parent CK_AND or CK_OR entry (or 0 if none). */ inline a_boolean no_link() const { return this->link == 0; } /* Convenience function to test the absence of a parent entry. */ union { uint32_t next; /* For CK_ATOMIC entries, the 1-based index of the next CK_ATOMIC entry that points to the same constraint. (If there is no other such entry, the index of this entry.) */ a_template_arg_ptr remapped_args; /* For CK_CONCEPT entries, a pointer to the remapped concept-id arguments, or NULL if they have not been computed yet. Remapped arguments are arguments substituted by parent concept-id arguments. */ a_boolean flag; /* For CK_AND and CK_OR entries, nonzero if the second constraint operand should be considered at this time. */ }; an_expr_node_ptr expr; /* An atomic constraint or an enk_concept_id node. */ }; /* a_charted_constraint */ static constexpr uint32_t constraint_link_bitmask = (uint32_t)bitmask_of_width(30); /* The bitmask for setting the link data member of a_charted_constraint. */ namespace detail { template<> struct Is_trivially_copyable_edg_impl : Integral_constant { }; /* Is_trivially_copyable_edg_impl */ template<> struct Is_trivially_destructible_edg_impl : Integral_constant { }; /* Is_trivially_destructible_edg_impl */ } /* namespace detail */ struct a_constraint_chart { inline a_constraint_chart(size_t cap) : constraints_array(cap), not_subsumable(FALSE), not_subsuming(FALSE) {} Dyn_array constraints_array; /* A representation of the constraint obtained by traversing the constraint expression tree(s) in prefix depth-first order. */ a_bit_field not_subsumable:1; /* TRUE if the constraints are not subsumable. This occurs if every disjunctive clause of the constraint has an atomic constraint that is not introduced by a concept. */ a_bit_field not_subsuming:1; /* TRUE if the constraints are not subsuming. This occurs if at least one of the conjunctive clauses contains no CK_CONCEPT entry. */ }; /* a_constraint_chart */ #define UNCONSTRAINED_CHART ((a_constraint_chart*)(uintptr_t)0x1) #if DEBUG /*lint -esym(714,*db_constraint_chart)*/ void db_constraint_chart(a_constraint_chart *chart) /* Output a description of the given constraint chart. */ { if (chart == UNCONSTRAINED_CHART) { fprintf(f_debug, "\n"); } else if (chart == NULL) { fprintf(f_debug, "\n"); } else { Dyn_array &array = chart->constraints_array; a_const_char *names[] = { "Atomic", "Concept", "AND", "OR" }; if (chart->not_subsumable) { fprintf(f_debug, "Not subsumable\n"); } /* if */ for (size_t i = 0; i %3d] %s ", (int)i, array[i].no_link() ? -1 : (int)array[i].link - 1, names[array[i].kind]); if (array[i].kind == CK_AND || array[i].kind == CK_OR) { fprintf(f_debug, "(flag = %d)\n", (int)array[i].flag); } else if (array[i].kind == CK_ATOMIC) { fprintf(f_debug, "(next = %d) %p\n", (int)array[i].next, (void*)array[i].expr); } else { fprintf(f_debug, "\n"); } /* if */ if (array[i].kind == CK_ATOMIC || array[i].kind == CK_CONCEPT) { db_expr_node(array[i].expr, 2); } /* if */ } /* for */ } /* if */ } /* db_constraint_chart */ /*lint -esym(714,*db_active_E1_constraint)*/ void db_active_E1_constraint(a_constraint_chart *chart, size_t k = 1) /* Output a description of the currently-active E1[k - 1] elements (ANDed atomic constraints). */ { if (chart == UNCONSTRAINED_CHART) { fprintf(f_debug, "\n"); } else if (chart == NULL) { fprintf(f_debug, "\n"); } else { Dyn_array &array = chart->constraints_array; while (array[k - 1].kind == CK_CONCEPT) ++k; if (array[k - 1].kind == CK_AND) { db_active_E1_constraint(chart, k+1); fprintf(f_debug, "AND &&\n"); db_active_E1_constraint(chart, array[k - 1].link); } else if (array[k - 1].kind == CK_OR) { if (array[k - 1].flag) { db_active_E1_constraint(chart, array[k - 1].link); } else { db_active_E1_constraint(chart, k+1); } /* if */ } else { fprintf(f_debug, "@ %lu (base 0):\n", (unsigned long)(k - 1)); db_expr_node(array[k - 1].expr, 2); } /* if */ } /* if */ } /* db_active_E1_constraint */ /*lint -esym(714,*db_active_E2_constraint)*/ void db_active_E2_constraint(a_constraint_chart *chart, size_t k = 1) /* Output a description of the currently-active E2[k - 1] elements (ORed atomic constraints). */ { if (chart == UNCONSTRAINED_CHART) { fprintf(f_debug, "\n"); } else if (chart == NULL) { fprintf(f_debug, "\n"); } else { Dyn_array &array = chart->constraints_array; while (array[k - 1].kind == CK_CONCEPT) ++k; if (array[k - 1].kind == CK_OR) { db_active_E2_constraint(chart, k+1); fprintf(f_debug, "OR ||\n"); db_active_E2_constraint(chart, array[k - 1].link); } else if (array[k - 1].kind == CK_AND) { if (array[k - 1].flag) { db_active_E2_constraint(chart, array[k - 1].link); } else { db_active_E2_constraint(chart, k+1); } /* if */ } else { fprintf(f_debug, "@ %lu (base 0):\n", (unsigned long)(k - 1)); db_expr_node(array[k - 1].expr, 2); } /* if */ } /* if */ } /* db_active_E2_constraint */ #endif /* DEBUG */ using a_constraint_charts_map = Ptr_map; /* The type of a map from constrained entity symbols to their associated constraint chart (if one has been constructed). */ STATIC_THREAD a_constraint_charts_map *constraint_charts; /* Map of constraint charts. */ /* A map type to map an expression to charted constraints. Since an expression can appear multiple times (with different mappings) in the constraint chart, the chart links identical expressions on a circular linked list through the "next" field. */ using an_expr_chart_map = Ptr_map; #if DEBUG /* Explicitly instantiate "an_expr_chart_map"'s db_ptrs function. Note that, grammatically, the type alias an_expr_chart_map cannot be used here. */ template void Ptr_map::db_ptrs() const; #endif /* DEBUG */ struct a_map_check_pair { uint32_t idx1, idx2; }; namespace detail { template<> struct Is_trivially_copyable_edg_impl : Integral_constant { }; /* Is_trivially_copyable_edg_impl */ template<> struct Is_trivially_destructible_edg_impl : Integral_constant { }; /* Is_trivially_destructible_edg_impl */ } /* namespace detail */ using a_map_check_list = Dyn_array; static uint32_t first_conjunctive_clause_term( Dyn_array *p_array, uint32_t curr_idx) /* *p_array is an array representing a flattened constraint whose conjunctive clauses (E2[k] in the outline above) we are traversing. Each such clause contains one or more ORed terms. Identify the first such term in the subtree rooted at curr_idx. I.e., return the 1-based index of the atomic constraint selected by the "flag" values of AND nodes when always selecting the left subtree of OR nodes. */ { Dyn_array &array = *p_array; for (;;) { a_charted_constraint *constraint = &array[curr_idx - 1]; switch (constraint->kind) { case CK_ATOMIC: goto done; case CK_CONCEPT: case CK_OR: /* Skip to the next node (which for CK_CONCEPT is the unique child node and for CK_OR is the left child node). */ ++curr_idx; break; case CK_AND: if (constraint->flag) { /* Select the right child. */ curr_idx = constraint->link; } else { /* Select the left child. */ ++curr_idx; } /* if */ break; default: unexpected_condition(); } /* switch */ } /* for */ done: return curr_idx; } /* first_conjunctive_clause_term */ static uint32_t next_conjunctive_clause_term( Dyn_array *p_array, uint32_t curr_idx, a_boolean *p_flipping) /* *p_array is an array representing a flattened constraint whose conjunctive clauses (E2[k] in the outline above) we are traversing. Each such clause contains one or more ORed terms and curr_idx is the 1-based index of the current term (a CK_ATOMIC entry). Return the 1-based index of the next term in the clause or 0 if there is none. If *p_flipping is TRUE, this routine will attempt to "flip" the "flag" fields in AND nodes as needed to prepare for the next conjunctive clause. */ { Dyn_array &array = *p_array; uint32_t prev_k = curr_idx, k = array[curr_idx - 1].parent_op; /* The "next term" is always in the right subtree of an OR node. The algorithm here is to walk up the tree until we reach an OR node from a left subtree (if there is none, we're done and return -1), and then return the left-most node in the corresponding right subtree. While walking up to the (potential) OR node, the first FALSE-flagged AND node is "flipped" to TRUE so that the next term will be selected for that AND node during the next clause traversal. */ while (k != 0) { a_charted_constraint *constraint = &array[k - 1]; uint32_t right_subtree; switch (constraint->kind) { case CK_OR: right_subtree = constraint->link; if (prev_k < right_subtree) { /* We're returning from the left child. The next term is the first term of the right subtree. */ prev_k = k; if (*p_flipping) { /* Consider (A & B) || (C & D). If we get here when *p_flipping still TRUE, it means the terms A & B have both been traversed (that AND had its flag already set and the current term is B). If the next term is C, we have to reset the AND flags for the (A & B) subtree so that after we have produced the B || C clause, we will restart from the A term to produce the A || D and B || D clauses. If the next term is D, that "reset" work is wasted, but we have no cheap way to detect that (and it's not that much work in the bigger picture of OR nodes). Note that the "subtree" to clear goes from k+1 (or prev_k) to constraint->link-1. */ while (++k < right_subtree) { if (constraint->kind == CK_AND) constraint->flag = FALSE; } /* while */ } /* if */ k = first_conjunctive_clause_term(p_array, right_subtree); goto done; } else { /* We're returning from the right child and are thus done with this OR node. Continue with the parent operation to see if there are more OR nodes. */ prev_k = k; k = constraint->parent_op; } /* if */ break; case CK_AND: if (!constraint->flag && *p_flipping) { /* We previously selected the left subtree. When forming the next clause, we'll select the right subtree. */ constraint->flag = TRUE; *p_flipping = FALSE; } /* if */ prev_k = k; k = array[k - 1].parent_op; break; default: unexpected_condition(); } /* switch */ } /* while */ done: return k; } /* next_conjunctive_clause_term */ static a_boolean process_conjunctive_clause(a_constraint_chart *chart, an_expr_chart_map *expr_map, a_map_check_list *map_checks) /* Check whether the ORed constraint nodes in the current conjunctive clause (E2[k] in the outline above) are present in expr_map. If so, append a pair (i, j) to map_checks, where i is the index that the constraint node is mapped to in expr_map, and j is the corresponding constraint in the current conjunctive clause. Also update the flags in the CK_AND entries for the next conjunctive clause, and if the last clause was processed return TRUE. */ { Dyn_array &array = chart->constraints_array; uint32_t k = 1; a_boolean flipping = TRUE; /* array (i.e., chart->constraints_array) contains a flattened representation of the constraint expression with concept-ids (which are skipped through in this process), AND/OR nodes, and atomic constraints (which must be matched). During this process, the AND nodes contain a flag that is FALSE if the "left" operand is active and TRUE if the "right" operand is active. At first all the flags are FALSE, which corresponds to a particular conjunctive clause. The loop below will either flip one such flag from FALSE to TRUE and set flipping to FALSE, or it will leave flipping set to TRUE, which means we reached the last conjunctive clause. This flipping is done in next_conjunctive_clause_term. If there are OR nodes, the flags in the left operand may also be reset to FALSE when all the terms in that left operand have been processed. For example, consider the constraint expression (A & B & C) | D. The initial state can be denoted as follows using prefix notation (i.e., the flattened representation): | (&0 (&0 A B) C) D Here &0 represents AND nodes with flag set to FALSE. The call to first_conjunctive_clause_term returns 3 (the position of the A term). The first call to next_conjunctive_clause_term returns 6 (term D) and changes the state to | (&0 (&1 A B) C) D (&1 is an AND node with a TRUE flag) and sets flipping to FALSE. The next call to next_conjunctive_clause_term returns -1, indicating that there are no more terms. Thus the loop produced the terms A | D. Since flipping is FALSE at the end of this process, there are more clauses. At the next call of this function, first_conjunctive_clause_term returns 4 (the position of the B term) and a similar process to the loop described above produces the terms B | D and flipping == FALSE, with the state as follows: | (&1 (&1 A B) C) D Finally, the third call of this function produces the terms C | D, but this time flipping comes out TRUE, which indicates that we produced the last conjunctive clause. Note that the state now looks like: | (&0 (&0 A B) C) D Since no AND nodes switched from &0 to &1, flipping is indeed TRUE. However, some AND nodes switched from &1 to &0: This is wasted work in this particular case, but if the right operand of the OR node were more complex it would be needed to iterate through the various possible combinations. */ k = first_conjunctive_clause_term(&array, 1); do { a_charted_constraint *constraint = &array[k - 1]; uint32_t idx; check_assertion(constraint->kind == CK_ATOMIC); idx = expr_map->get(constraint->expr); if (idx != 0) { /* The active atomic constraint at idx in chart1 corresponds to this active atomic constraint in chart2 (where chart1 describes E1 and chart2 describes E2 in the outline above). Keep checking for matching constraints, but record that we should check the mappings of these constraints after we find all matches. */ map_checks->push_back(a_map_check_pair{ idx, k }); } /* if */ k = next_conjunctive_clause_term(&array, k, &flipping); } while (k != 0); return flipping; } /* process_conjunctive_clause */ static uint32_t first_disjunctive_clause_term( Dyn_array *p_array, uint32_t curr_idx) /* *p_array is an array representing a flattened constraint whose disjunctive clauses (E1[k] in the outline above) we are traversing. Each such clause contains one or more ANDed terms. Identify the first such term in the subtree rooted at curr_idx. I.e., return the index of the atomic constraint selected by the "flag" values of OR nodes when always selecting the left subtree of AND nodes. */ { Dyn_array &array = *p_array; for (;;) { a_charted_constraint *constraint = &array[curr_idx - 1]; switch (constraint->kind) { case CK_ATOMIC: goto done; case CK_CONCEPT: case CK_AND: /* Skip to the next node (which for CK_CONCEPT is the unique child node and for CK_AND is the left child node). */ ++curr_idx; break; case CK_OR: if (constraint->flag) { /* Select the right child. */ curr_idx = constraint->link; } else { /* Select the left child. */ ++curr_idx; } /* if */ break; default: unexpected_condition(); } /* switch */ } /* for */ done: return curr_idx; } /* first_disjunctive_clause_term */ static uint32_t next_disjunctive_clause_term( Dyn_array *p_array, uint32_t curr_idx, a_boolean *p_flipping) /* *p_array is an array representing a flattened constraint whose disjunctive clauses (E1[k] in the outline above) we are traversing. Each such clause contains one or more ANDed terms and curr_idx is the current term (a CK_ATOMIC entry). Return the 1-based index of the next term in the clause or 0 if there is none. If *p_flipping is TRUE, this routine will attempt to "flip" the "flag" fields in OR nodes as needed to prepare for the next conjunctive clause. */ { Dyn_array &array = *p_array; uint32_t prev_k = curr_idx, k = array[curr_idx - 1].parent_op; /* The "next term" is always in the right subtree of an AND node. The algorithm here is to walk up the tree until we reach an AND node from a left subtree (if there is none, we're done and return -1), and then return the left-most node in the corresponding right subtree. While walking up to the (potential) AND node, the first FALSE-flagged OR node is "flipped" to TRUE so that the next term will be selected for that OR node during the next clause traversal. */ while (k != 0) { a_charted_constraint *constraint = &array[k - 1]; uint32_t right_subtree; switch (constraint->kind) { case CK_AND: right_subtree = constraint->link; if (prev_k < right_subtree) { /* We're returning from the left child. The next term is the first term of the right subtree. */ prev_k = k; k = first_disjunctive_clause_term(p_array, right_subtree); goto done; } else { /* We're returning from the right child and are thus done with this AND node. Continue with the parent operation to see if there are more AND nodes. */ prev_k = k; k = constraint->parent_op; } /* if */ break; case CK_OR: if (!constraint->flag && *p_flipping) { /* We previously selected the left subtree. When forming the next clause, we'll select the right subtree. */ constraint->flag = TRUE; *p_flipping = FALSE; } /* if */ prev_k = k; k = array[k - 1].parent_op; break; default: unexpected_condition(); } /* switch */ } /* while */ done: return k; } /* next_disjunctive_clause_term */ static a_boolean process_disjunctive_clause(a_constraint_chart *chart, an_expr_chart_map *expr_map) /* Record the ANDed atomic constraints in the current disjunctive clause (E1[k] in the outline above) in expr_map, and update the flags (in OR nodes) for the next clause. Return TRUE if the last clause was processed. */ { Dyn_array &array = chart->constraints_array; uint32_t k = 1; a_boolean flipping = TRUE; /* The following loop does two things: 1) It flips flags on the CK_OR nodes thereby setting up the next disjunctive clause, and 2) it records the expressions ("atomic constraints") of the current disjunctive clause of E1/chart1 in a map so they can be efficiently looked up when comparing to the conjunctive clauses in the normal form of E2/chart2. The iteration over all disjunctive clauses is the exact complement of the process in process_conjunctive_clause: See that function for an overview of that process. */ k = first_disjunctive_clause_term(&array, 1); do { a_charted_constraint *constraint = &array[k - 1]; uint32_t prev_k; check_assertion(constraint->kind == CK_ATOMIC); prev_k = expr_map->map_or_replace(constraint->expr, k); if (prev_k != 0) { /* This particular atomic constraint appears more than once among the terms (possibly with different template parameter mappings). Link the terms in a circular list. */ a_charted_constraint *prev_constraint = &array[prev_k - 1]; constraint->next = prev_constraint->next; prev_constraint->next = k; } /* if */ k = next_disjunctive_clause_term(&array, k, &flipping); } while (k != 0); return flipping; } /* process_disjunctive_clause */ static a_template_arg_ptr get_remapped_args(a_constraint_chart *chart, uint32_t idx) /* Return remapped concept-id arguments for the concept-id constraint at the given index in the given constraint chart. */ { Dyn_array &array = chart->constraints_array; a_charted_constraint *constraint = &array[idx - 1], *parent_constraint; a_template_arg_ptr result = constraint->remapped_args; if (result == NULL) { an_expr_node_ptr concept_id = constraint->expr, parent_concept_id; a_template_ptr templ, parent_templ; a_symbol_ptr sym, parent_sym; a_template_arg_ptr orig_args, parent_remapped_args; templ = concept_id->variant.concept_id.concept_template; sym = symbol_for(templ); orig_args = concept_id->variant.concept_id.args; if (constraint->no_link()) { /* A top-level concept-id: No remapping is needed. */ result = orig_args; } else { a_ctws_state ctws_state; a_boolean copy_error = FALSE; a_template_param_ptr params = sym->variant.template_info->cache-> decl_info->parameters; check_assertion(!constraint->no_link()); parent_constraint = &array[constraint->link - 1]; parent_concept_id = parent_constraint->expr; parent_templ = parent_concept_id->variant.concept_id.concept_template; parent_sym = symbol_for(parent_templ); a_template_param_ptr parent_params = parent_sym->variant.template_info-> cache->decl_info->parameters; parent_remapped_args = get_remapped_args(chart, constraint->link); /* Substitute the dependent arguments of the concept-id. */ init_ctws_state(&ctws_state); result = copy_template_arg_list_with_substitution( sym, orig_args, params, (a_template_param_ptr)NULL, parent_remapped_args, parent_params, &concept_id->position, CTWS_NO_OPTIONS, ©_error, &ctws_state); } /* if */ /* Cache the remapping so it can be reused if this constraint chart is needed again. */ constraint->remapped_args = result; } /* if */ return result; } /* get_remapped_args */ static a_boolean incompatible_mappings(a_constraint_chart *chart1, a_constraint_chart *chart2, a_map_check_list *map_checks) /* The constraints represented by chart1 subsume those represented by chart2 if the parameter mappings of certain atomic expression pairs are equivalent. map_checks represent the pairs (e1, e2) that must be checked, except that e1 is a representative of a circular list of identical expressions that may appear in different mapping contexts: That list is connected by the "next" field in the corresponding chart entries. Return TRUE if there is any entry e2 such that none of the corresponding entries in chart1 has an equivalent parameter mapping. */ { a_boolean result = TRUE; Dyn_array &array1 = chart1->constraints_array, &array2 = chart2->constraints_array; push_instantiation_scope_for_rescan((a_symbol_ptr)NULL); for (a_map_check_pair &p: *map_checks) { uint32_t idx1 = p.idx1, idx2 = p.idx2, orig_idx1 = idx1; a_template_arg_ptr args1, args2; args2 = get_remapped_args(chart2, array2[idx2 - 1].link); /* Loop through the linked list of identical chart1 atomic constraint expressions. */ do { args1 = get_remapped_args(chart1, array1[idx1 - 1].link); /* We really only should check the arguments used by the atomic constraints, but for now we check them all. */ if (equiv_template_arg_lists(args1, args2, ETA_ALLOW_EQUIV_NESTING_DEPTHS)) { result = FALSE; goto done; } /* if */ idx1 = array1[idx1 - 1].next; } while (idx1 != orig_idx1); } /* for */ done: pop_instantiation_scope_for_rescan(); return result; } /* incompatible_mappings */ static a_boolean subsumes_constraint_chart(a_constraint_chart *chart1, a_constraint_chart *chart2) /* Return TRUE if the constraints charted in chart1 subsume those in chart2. Return FALSE otherwise. */ { a_boolean result; if (chart2->not_subsumable) { result = FALSE; } else { Dyn_array &array1 = chart1->constraints_array, &array2 = chart2->constraints_array; uint32_t i, len; /* Clear the CK_OR flags in chart1 and the CK_AND flags in chart2. Also initialize the "next" fields for the atomic expressions. */ len = (uint32_t)array1.length(); for (i = 0; ikind == CK_OR) { constraint->flag = FALSE; } else if (constraint->kind == CK_ATOMIC) { constraint->next = i + 1; } /* if */ } /* if */ len = (uint32_t)array2.length(); for (i = 0; ikind == CK_AND) { constraint->flag = FALSE; } else if (constraint->kind == CK_ATOMIC) { constraint->next = i + 1; } /* if */ } /* if */ /* Implement the algorithm: result = TRUE for each E1[i] for each E2[j] if not (E1[i] SS E2[j]) result = FALSE exit described above. */ result = TRUE; for (;;) { /* Loop through the disjunctive clauses of chart1. E.g., if chart1 corresponds to constraint "(A && B) || (C && D)", then the first time through we'll select "(A && B)" and the second time through we'll select "(C && D)"; that means that the first time through expr_map will contain the expressions underlying A and B, and the second time through the expressions underlying C && D. If chart1 instead corresponds to constraint "((A || B) && C) || D", we'll consecutively activate the expressions underlying A&&C, B&&C, D, D (which represents the -- admittedly inefficient -- disjunctive normal form "(A && C) || (B && C) || D || D" of the constraint. */ an_expr_chart_map expr_map(/*mask_width=*/3); a_boolean last_disj_clause = process_disjunctive_clause(chart1, &expr_map); for (;;) { /* Loop through the conjunctive clauses of chart2. E.g., if chart2 corresponds to constraint "(A || B) && C", the first time through will select "(A || B)" and the second time through will select C. map_checks will contain the matching constraint expressions, but they only really match if their parameter mappings are compatible. */ a_map_check_list map_checks(10); a_boolean last_conj_clause = process_conjunctive_clause( chart2, &expr_map, &map_checks); if (map_checks.length() == 0) { /* No matching atomic constraints were found. */ result = FALSE; goto done; } else if (incompatible_mappings(chart1, chart2, &map_checks)) { result = FALSE; goto done; } /* if */ if (last_conj_clause) break; } /* for */ if (last_disj_clause) break; /* Reset the "common atomic expression" links for the next disjunctive clause (the sets should only involve active terms). */ len = (uint32_t)array1.length(); for (i = 0; ikind == CK_ATOMIC) constraint->next = i + 1; } /* for */ } /* for */ } /* if */ done: return result; } /* subsumes_constraint_chart */ static void chart_constraint(an_expr_node_ptr expr, a_constraint_chart *chart, uint32_t parent_idx, uint32_t parent_op, a_boolean *not_subsumable, a_boolean *not_subsuming) /* Perform a recursive prefix traversal of the given (constraint) expression and chart its significant nodes (concept-ids, conjunctions, disjunctions, and atomic constraints) into *chart. parent_idx is the 1-based index of the last concept-id traversed prior to expr (0 if there was none). parent_op is the 1-based index of the last OR or AND node traversed prior to expr (0 if there was none). not_subsumable is set to TRUE if an atomic constraint is encountered with parent_idx == 0, or if a disjunction is encountered where both alternatives recursively produced a not_subsumable == TRUE result. not_subsuming is set to FALSE if a concept use is encountered with parent_idx == 0 or if a conjunction is encountered where both alternatives recursively set not_subsuming to FALSE. */ { Dyn_array &array = chart->constraints_array; if (node_is(expr, enk_concept_id)) { array.push_back(a_charted_constraint{ CK_CONCEPT, parent_idx & constraint_link_bitmask, parent_op, { (uint32_t)0 }, expr }); uint32_t new_parent_idx = (uint32_t)array.length(); array[new_parent_idx - 1].remapped_args = nullptr; if (parent_idx == 0) { /* A concept at the top level creates a potential for subsuming another template. */ *not_subsuming = FALSE; /* If this constraint is a type constraint, record the concept-id arguments (which need no remapping since they're at the top level). For a type constraint, the first argument was made explicit in constraint_chart_of, but it should be made implicit again in the concept-id node. */ a_template_arg_ptr args = expr->variant.concept_id.args; array[new_parent_idx - 1].remapped_args = args; if (expr->is_type_constraint) { expr->variant.concept_id.args = args->next; } /* if */ } else { /* Remapped arguments will be computed if needed later on. */ array[new_parent_idx - 1].remapped_args = nullptr; } /* if */ chart_constraint(expr->variant.concept_id.concept_template ->prototype_instantiation.constraint, chart, new_parent_idx, parent_op, not_subsumable, not_subsuming); } else if (is_operation_node(expr) && node_operator_is(expr, eok_land)) { a_boolean left_not_subsuming = TRUE, right_not_subsuming = TRUE; array.push_back(a_charted_constraint{ CK_AND, ((uint32_t)parent_idx & constraint_link_bitmask), parent_op, { (uint32_t)0 }, expr }); uint32_t idx = (uint32_t)array.length(); an_expr_node_ptr opnds = expr->variant.operation.operands; chart_constraint(opnds, chart, parent_idx, idx, not_subsumable, &left_not_subsuming); array[idx - 1].link = ((uint32_t)(array.length() + 1) & constraint_link_bitmask); chart_constraint(opnds->next, chart, parent_idx, idx, not_subsumable, &right_not_subsuming); if (!left_not_subsuming && !right_not_subsuming) { *not_subsuming = FALSE; } /* if */ } else if (is_operation_node(expr) && node_operator_is(expr, eok_lor)) { a_boolean left_not_subsumable = FALSE, right_not_subsumable = FALSE; array.push_back(a_charted_constraint{ CK_OR, parent_idx & constraint_link_bitmask, parent_op, { (uint32_t)0 }, expr }); uint32_t idx = (uint32_t)array.length(); an_expr_node_ptr opnds = expr->variant.operation.operands; chart_constraint(opnds, chart, parent_idx, idx, &left_not_subsumable, not_subsuming); array[idx - 1].link = ((uint32_t)(array.length() + 1) & constraint_link_bitmask); chart_constraint(opnds->next, chart, parent_idx, idx, &right_not_subsumable, not_subsuming); if (left_not_subsumable && right_not_subsumable) { *not_subsumable = TRUE; } /* if */ } else { /* An atomic constraint. */ array.push_back(a_charted_constraint{ CK_ATOMIC, parent_idx & constraint_link_bitmask, parent_op, { (uint32_t)0 }, expr }); if (parent_idx == 0) *not_subsumable = TRUE; } /* if */ } /* chart_constraint */ static a_constraint_chart* constraint_chart_of(a_symbol_ptr sym) /* Return the constraint chart for the given declaration (if any). If needed, generate that chart. */ { a_constraint_chart *result = constraint_charts->get(sym); if (result == NULL) { Dyn_array constraints(10); if (is_template_symbol(sym)) { a_template_symbol_supplement_ptr tssp = sym->variant.template_info; a_template_ptr il_entry = tssp->il_template_entry; a_template_decl_ptr tdp = il_entry->template_decl; a_requires_clause_ptr rcp = NULL; if (tdp != NULL if_microsoft_extensions(&& !tdp->is_generic)) { rcp = tdp->constraint.requires_clause; } /* if */ if (tssp->has_template_param_constraint) { /* Collect any type constraints on the parameters. */ a_boolean auto_param_seen = FALSE; a_template_param_ptr tpp, params = templ_params_of(sym); for (tpp = params; tpp != NULL; tpp = tpp->next) { an_expr_node_ptr type_constraint = NULL; if (symbol_is(tpp->param_symbol, sk_type)) { a_type_ptr tp = tpp->variant.type; type_constraint = tp->variant.template_param.extra_info ->constraint.type_constraint; if (!auto_param_seen && tp->variant.template_param.is_auto_param) { /* The template requires clause comes between ordinary template parameter type constraints and type constraints resulting from abbreviated function template "auto" parameters. */ if (rcp != NULL) { constraints.push_back(rcp->constraint); rcp = NULL; } /* if */ auto_param_seen = TRUE; } /* if */ if (type_constraint != NULL) { constraints.push_back(type_constraint); /* Temporarily make the first template argument explicit. This will be restored in chart_constraint. */ a_template_arg_ptr first_arg; first_arg = alloc_template_arg((a_templ_arg_kind)tak_type); first_arg->variant.type = tp; first_arg->next = type_constraint->variant.concept_id.args; type_constraint->variant.concept_id.args = first_arg; } /* if */ } /* if */ } /* for */ } /* if */ if (rcp != NULL) { constraints.push_back(rcp->constraint); } /* if */ if (symbol_is(sym, sk_function_template)) { a_routine_ptr rp = tssp->variant.function.routine; if (rp->trailing_requires_clause != NULL) { constraints.push_back(rp->trailing_requires_clause->constraint); } /* if */ } /* if */ } else if (is_simple_function_symbol(sym)) { a_routine_ptr rp = sym->variant.routine.ptr; if (rp->trailing_requires_clause != NULL) { constraints.push_back(rp->trailing_requires_clause->constraint); } /* if */ } else { unexpected_condition(); } /* if */ uint32_t n_constraints = (uint32_t)constraints.length(); if (n_constraints == 0) { result = UNCONSTRAINED_CHART; } else { a_boolean not_subsumable = FALSE, not_subsuming = TRUE; result = new_fe(2*n_constraints); Dyn_array &array = result->constraints_array; uint32_t prev_AND_pos = 0; for (uint32_t i = 0; inot_subsumable = TRUE; } /* if */ if (not_subsuming) { result->not_subsuming = TRUE; } /* if */ } /* if */ constraint_charts->map(sym, result); } /* if */ return result; } /* constraint_chart_of */ int compare_constraints(a_symbol_ptr sym1, a_symbol_ptr sym2, /* Defaulted: */ a_boolean *p_equiv) /* Return +1 if sym1 is more constrained than sym2, -1 if sym2 is more constrained than sym1, and 0 otherwise. If p_equiv (defaulted to NULL) is non-NULL, set *p_equiv to TRUE if the constraints of sym1 subsume those of sym2 and vice versa; otherwise, set it to FALSE. */ { a_constraint_chart *chart1, *chart2; int result = 0; a_boolean equiv = FALSE; sym1 = originator_symbol_of(sym1); sym2 = originator_symbol_of(sym2); chart1 = constraint_chart_of(sym1); chart2 = constraint_chart_of(sym2); if (chart1 == UNCONSTRAINED_CHART || chart2 == UNCONSTRAINED_CHART) { if (chart1 != UNCONSTRAINED_CHART) { result = +1; } else if (chart2 != UNCONSTRAINED_CHART) { result = -1; } else { /* Two unconstrained templates. The "constraints" are unordered, but equivalent. */ result = 0; equiv = TRUE; } /* if */ } else if (sym1 != sym2) { /* Both declarations are constrained: Check subsumption. */ a_boolean sym1_subsumes_sym2 = subsumes_constraint_chart(chart1, chart2), sym2_subsumes_sym1 = subsumes_constraint_chart(chart2, chart1); if (sym1_subsumes_sym2 && !sym2_subsumes_sym1) { result = +1; } else if (!sym1_subsumes_sym2 && sym2_subsumes_sym1) { result = -1; } else { result = 0; equiv = sym1_subsumes_sym2; } /* if */ } /* if */ if (p_equiv != NULL) *p_equiv = equiv; return result; } /* compare_constraints */ struct a_constraint_test { /* A structure that encodes a constraint test: i.e., a constraint expression and template arguments to substitute in that expression. */ an_expr_node_ptr constraint; /* The unsubstituted constraint expression. */ an_expr_node_ptr expr; /* The partially-substituted expression being checked for satisfaction. */ a_template_param_ptr template_param_list; /* The template parameters for the substitution. */ a_template_arg_ptr template_arg_list; /* The template arguments for the substitution. */ }; static inline a_boolean operator==(a_constraint_test ct1, a_constraint_test ct2) /* Return TRUE if the given constraint tests are equivalent. */ { a_boolean result; if (ct1.constraint != ct2.constraint) { result = FALSE; } else if (ct1.template_param_list != ct2.template_param_list) { result = FALSE; } else if (ct1.expr != ct2.expr && !compare_expressions(ct1.expr, ct2.expr, (CC_STRICTLY_IDENTICAL | CC_EXACT_EQUIVALENCE | CC_EXACT_DECLTYPE_EXPR_MATCH_REQUIRED))) { result = FALSE; } else if (ct1.template_arg_list == ct2.template_arg_list) { result = TRUE; } else if (ct1.template_arg_list == NULL || ct2.template_arg_list == NULL) { result = FALSE; } else { result = equiv_template_arg_lists( ct1.template_arg_list, ct2.template_arg_list, (ETA_EXACT_MATCH_REQUIRED | ETA_EXACT_DECLTYPE_EXPR_MATCH_REQUIRED)); } /* if */ return result; } /* operator== */ static inline a_boolean operator!=(a_constraint_test ct1, a_constraint_test ct2) /* Return TRUE if the given constraint tests are not equivalent. */ { return !(ct1 == ct2); } /* operator!= */ static inline uintptr_t hash_ptr(a_constraint_test ct) /* Return a hash value for a constraint test description. */ { uintptr_t result = 17*31 + hash_ptr((void*)ct.constraint); result = result*31 + hash_ptr((void*)ct.template_param_list); result = result*31 + (uintptr_t)hash_expr(ct.expr); result = result*31 + (uintptr_t)hash_template_arg_list(ct.template_arg_list); return result; } /* hash_ptr */ enum class a_test_constraint_result { /* An enumeration describing a previously substituted and evaluated constraint expression. */ none, /* This represents the null (default) value. */ pending, /* Satisfaction checking has started but not completed. This result is updated when satisfaction checking completes. */ subst_failed, /* Substitution has failed. */ eval_failed, /* Evaluation has failed. */ nonbool_result, /* Evaluation yielded a non-boolean result. */ not_satisfied, /* Constraint is not satisfied. */ satisfied /* Constraint is satisfied. */ }; using a_constraint_satisfaction_cache = Ptr_map; /* The type of a map that caches the satisfaction of constraints. */ STATIC_THREAD a_constraint_satisfaction_cache *constraint_satisfaction_cache; /* A map from constraint test descriptions to satisfaction results. */ a_boolean is_concept_satisfied(an_expr_node_ptr constraint, a_template_arg_ptr args, a_diag_list_ptr diag_list, a_ctws_options_set options, a_ctws_state_ptr ctws_state, a_boolean *p_fatal) /* Return TRUE if the given concept is satisfied by the given template arguments. Otherwise, return FALSE and: - if diag_list is non-NULL, update diag_list with notes describing the reason for the failure, and - if the failure is not subject to SFINAE, set *p_fatal to TRUE. options is a set of substitution options. ctws_state is a substitution state block pointer. */ { a_boolean result = FALSE, do_not_cache_result = FALSE; a_diagnostic_ptr prev_diags = NULL; a_template_ptr templ = constraint->variant.concept_id.concept_template; a_symbol_ptr sym = symbol_for(templ); a_template_param_ptr params = sym->variant.template_info->cache-> decl_info->parameters; an_expr_node_ptr expr = templ->prototype_instantiation.constraint; a_constraint_test test = { constraint, constraint, params, args }; uintptr_t hash = hash_ptr(test); a_test_constraint_result cached_result; /* First check for a cached result. */ cached_result = constraint_satisfaction_cache->get_with_hash(test, hash); if (cached_result == a_test_constraint_result::not_satisfied && diag_list != NULL) { /* Do not use a negative cached result if the caller has passed in a diagnostics list. */ prev_diags = diag_list->tail; cached_result = a_test_constraint_result::none; do_not_cache_result = TRUE; } /* if */ switch (cached_result) { case a_test_constraint_result::satisfied: result = TRUE; break; case a_test_constraint_result::not_satisfied: result = FALSE; break; case a_test_constraint_result::none: { /* Evaluate the resulting constraint. */ a_diag_list concept_diag_list; if (diag_list == NULL) { clear_diag_list(&concept_diag_list); } /* if */ push_instantiation_scope_for_rescan(symbol_for(templ)); scope_stack_top().in_concept_rescan = TRUE; update_template_param_symbols(params, args); result = constraint_satisfied(expr, args, params, (diag_list != NULL) ? diag_list : &concept_diag_list, options, ctws_state, p_fatal); restore_default_template_params(params, /*packs_only=*/FALSE); pop_instantiation_scope_for_rescan(); if (diag_list == NULL) { discard_more_info_list(&concept_diag_list); } /* if */ if (!do_not_cache_result && in_file_scope(constraint)) { cached_result = result ? a_test_constraint_result::satisfied : a_test_constraint_result::not_satisfied; test.template_arg_list = copy_template_arg_list(args); (void)constraint_satisfaction_cache->map_or_replace_with_hash( test, cached_result, hash); } /* if */ } break; default: unexpected_condition(); } /* switch */ if (!result && !*p_fatal && diag_list != NULL) { an_ovl_res_descr_ptr descr = ovl_res_descr(); if (descr.has_value()) { /* A caller will add a note if needed. */ } else { /* Insert a diagnostic before the ones detailing the constraint failure. */ a_diag_list new_diags; clear_diag_list(&new_diags); more_info_tap_diagnostic(ec_concept_failed, &constraint->position, copy_template_arg_list(args), &new_diags); splice_diag_list(&new_diags, diag_list, prev_diags); } /* if */ } /* if */ return result; } /* is_concept_satisfied */ a_boolean is_substituted_concept_satisfied( an_expr_node_ptr constraint, a_subst_pairs_array const &subst_pairs, a_diag_list_ptr diag_list, a_ctws_options_set options, a_ctws_state_ptr ctws_state, a_boolean *p_fatal) /* Return TRUE if the given concept is satisfied after its template arguments are substituted by the given substitution pairs. Otherwise, return FALSE and: - if diag_list is non-NULL, update diag_list with notes describing the reason for the failure, and - if the failure is not subject to SFINAE, set *p_fatal to TRUE. options is a set of substitution options. ctws_state is a substitution state block pointer. */ { a_boolean result = FALSE, copy_error = FALSE; a_template_ptr templ = constraint->variant.concept_id.concept_template; a_symbol_ptr sym = symbol_for(templ); a_template_param_ptr params = sym->variant.template_info-> cache->decl_info->parameters; a_template_arg_ptr old_args = constraint->variant.concept_id.args; an_owned_template_arg_list new_args; an_ovl_res_descr_ptr descr; /* Substitute the template argument list of the concept, and then check the satisfaction of the concept's constraint expression with that substituted argument list. */ if (!subst_pairs.is_empty()) { /* Substitute the dependent arguments of the concept-id. */ a_ctws_state_ptr args_ctws_state = ctws_state; a_ctws_state new_ctws_state; a_boolean saved_in_concept_rescan = scope_stack_top().in_concept_rescan; scope_stack_top().in_concept_rescan = TRUE; if (args_ctws_state == NULL) { init_ctws_state(&new_ctws_state); args_ctws_state = &new_ctws_state; } /* if */ /* Substitute the concept-id's original arguments. We should really only substitute the parameters that are actually referenced by the atomic constraint (presumably, those are those for which param->param_symbol->referenced is TRUE), but for now we substitute them all. */ new_args = templ_args_after_substitutions(sym, old_args, params, (a_template_param_ptr)NULL, subst_pairs, &constraint->position, options, ©_error, args_ctws_state); scope_stack_top().in_concept_rescan = saved_in_concept_rescan; } else { /* The concept-id is already fully non-dependent. */ new_args = copy_template_arg_list(old_args); } /* if */ descr = ovl_res_descr(); if (copy_error) { if (diag_list != NULL) { /* Substitution errors during parameter mappings are not SFINAE-like if they occur outside atomic constraints. For example: template concept X = sizeof(T) == 1; template concept Y = X; template requires Y void f(T) {} void f(...); void g() { f(0); } fails at this stage when forming the invalid type T = "int &[1]" in the parameter mapping for concept X. (Ordinary SFINAE still applies, however.) */ more_info_tap_diagnostic(ec_concept_arg_list_substitution_failed, &constraint->position, copy_template_arg_list( subst_pairs.front_elem().args), diag_list); if (descr.has_value() && descr->emit_note_diagnostics) { splice_diag_list(diag_list, &descr->notes, descr->curr_diagnostic != NULL ? descr->curr_diagnostic : descr->notes.tail); clear_diag_list(diag_list); } /* if */ } /* if */ result = FALSE; } else { result = is_concept_satisfied(constraint, new_args.raw(), diag_list, options, ctws_state, p_fatal); if (!result) { if (descr.has_value() && descr->emit_note_diagnostics) { a_diag_list new_note = { NULL, NULL }, *notes = &descr->notes; more_info_sym_tap_diagnostic(ec_concept_not_satisfied, &constraint->position, sym, copy_template_arg_list(new_args.raw()), &new_note); splice_diag_list(&new_note, notes, descr->curr_diagnostic != NULL ? descr->curr_diagnostic : notes->tail); } /* if */ } /* if */ } /* if */ return result; } /* is_substituted_concept_satisfied */ a_boolean constraint_satisfied_full(an_expr_node_ptr constraint, a_subst_pairs_array const &subst_pairs, a_diag_list_ptr diag_list, /* Defaulted: */ a_ctws_options_set options, a_ctws_state_ptr ctws_state, a_boolean *p_fatal, a_boolean *p_copy_error) /* Return TRUE if the given constraint expression is satisfied by the given substitution pairs. Otherwise, return FALSE and: - if p_copy_error is non-NULL, set *p_copy_error to TRUE if the FALSE result is due to a substitution failure, or to FALSE otherwise; - update diag_list with notes describing the reason for the failure if that failure is a "SFINAE" failure; - if p_fatal is non-NULL and the failure is not subject to SFINAE, set *p_fatal to TRUE and update diag_list with a corresponding note, or - if p_fatal is NULL and the failure is not subject to SFINAE, issue an error with any notes recorded in diag_list and clear diag_list. options is a set of substitution options; it is CTWS_NO_OPTIONS by default. ctws_state is a substitution state block pointer; it is NULL by default. p_fatal and p_copy_error are NULL by default. */ { a_boolean result = TRUE, fatal = FALSE, diagnose_here = (p_fatal == NULL), copy_error = FALSE; if (diagnose_here) { p_fatal = &fatal; } /* if */ constraint = skip_parens(constraint); if (node_is(constraint, enk_concept_id)) { result = is_substituted_concept_satisfied(constraint, subst_pairs, diag_list, options, ctws_state, p_fatal); } else if (node_is_operator(constraint, eok_land)) { /* Check the two underlying constraints separately. If the first determines the outcome, the second is neither substituted nor evaluated. */ an_expr_node_ptr opnds = constraint->variant.operation.operands; result = constraint_satisfied_full(opnds, subst_pairs, diag_list, options, ctws_state, p_fatal, ©_error) && constraint_satisfied_full(opnds->next, subst_pairs, diag_list, options, ctws_state, p_fatal, ©_error); } else if (node_is_operator(constraint, eok_lor)) { /* Check the two underlying constraints separately. If the first determines the outcome, the second is neither substituted nor evaluated. */ an_expr_node_ptr opnds = constraint->variant.operation.operands; result = constraint_satisfied_full(opnds, subst_pairs, diag_list, options, ctws_state, p_fatal, ©_error); if (!result && !*p_fatal) { /* Evaluate the right side of the disjunction. This is an independent evaluation that should have its own notes sequence if needed. */ an_ovl_res_descr_ptr descr = ovl_res_descr(); a_boolean notes_pass = FALSE; a_diagnostic_ptr saved_curr_diagnostic = NULL; if (descr.has_value() && descr->emit_note_diagnostics) { notes_pass = TRUE; saved_curr_diagnostic = descr->curr_diagnostic; descr->curr_diagnostic = descr->notes.tail; } /* if */ result = constraint_satisfied_full(opnds->next, subst_pairs, diag_list, options, ctws_state, p_fatal, ©_error); if (notes_pass) { ovl_res_descr()->curr_diagnostic = saved_curr_diagnostic; } /* if */ } /* if */ } else { /* An atomic constraint. First perform substitution (or reuse a cached substitution); then evaluate the expression. */ an_expr_node_ptr expr = NULL; a_constant_ptr cp = local_constant(); a_constant_ptr allocated_cp = NULL; a_test_constraint_result constraint_result; an_ovl_res_stack *ovl_stack = ovl_res_stack(); a_boolean notes_pass = FALSE; a_template_arg_ptr template_arg_list = NULL; a_diag_list interpret_diag_list = { NULL, NULL }; if (ovl_stack != NULL && !ovl_stack->is_empty() && ovl_stack->top()->emit_note_diagnostics) { /* Overload resolution failed and we are repeating constraint checking to collect diagnostic notes. */ notes_pass = TRUE; } /* if */ if (!subst_pairs.is_empty()) { template_arg_list = subst_pairs.front_elem().args; } /* if */ if (subst_pairs.length() > 1) { /* We have nested template arguments. Perform ordinary expression substitutions on all but the outermost one. Treat these substitutions as nonreal: since the outermost substitution is still pending, a template-id in the constraint that still refers to an as-yet-unsubstituted enclosing template parameter must be left dependent rather than resolved to a real instance. */ a_ctws_state_ptr inner_ctws_state = ctws_state; a_ctws_state new_ctws_state; a_subst_pairs_array new_subst_pairs(subst_pairs.length() - 1); a_boolean saved_nonreal = scope_stack_top().in_nonreal_instantiation; if (inner_ctws_state == NULL) { init_ctws_state(&new_ctws_state); inner_ctws_state = &new_ctws_state; } /* if */ new_subst_pairs.insert(0, subst_pairs.begin() + 1, subst_pairs.length() - 1); scope_stack_top().in_nonreal_instantiation = TRUE; expr = substitute_expr(constraint, new_subst_pairs, inner_ctws_state, options | CTWS_MAY_BE_RESCANNED, cp, &allocated_cp, ©_error); scope_stack_top().in_nonreal_instantiation = saved_nonreal; if (!copy_error && expr == NULL) { if (allocated_cp == NULL) { /* The constant result was constructed in *cp: Move it to file scope memory. */ allocated_cp = move_local_constant_to_il(&cp); cp = local_constant(); } /* if */ expr = alloc_node_for_constant(allocated_cp); } /* if */ allocated_cp = NULL; } else { expr = constraint; } /* if */ if (!copy_error) { a_constraint_test test = { constraint, expr, NULL, NULL }; uintptr_t hash = 0; a_template_param_ptr template_param_list = NULL; a_test_constraint_result cached_result; if (!subst_pairs.is_empty()) { template_param_list = subst_pairs.front_elem().params; test.template_arg_list = template_arg_list; test.template_param_list = template_param_list; } /* if */ /* Defer instantiations during constraint checking. */ defer_instantiations++; if (notes_pass) { /* Re-evaluate the constraint (even if it was cached before) because we want to record a note for the one that failed. */ cached_result = a_test_constraint_result::none; constraint_result = a_test_constraint_result::none; } else { hash = hash_ptr(test); /* First check for a cached result. */ cached_result = constraint_satisfaction_cache ->get_with_hash(test, hash); constraint_result = cached_result; } /* if */ if (constraint_result == a_test_constraint_result::pending) { /* This constraint appears to depend on itself. */ copy_error = TRUE; } else if (constraint_result == a_test_constraint_result::none) { constraint_result = a_test_constraint_result::pending; /* In some (error) situations, the call to copy_template_param_expr may have caused the constraint test to be cached already. We therefore use "map_or_replace" instead of just "map" here. */ test.template_arg_list = copy_template_arg_list(template_arg_list); if (!notes_pass) { (void)constraint_satisfaction_cache->map_or_replace_with_hash( test, constraint_result, hash); } /* if */ if (!subst_pairs.is_empty()) { /* This is a new substitution. */ a_ctws_state new_ctws_state; a_source_position saved_err_pos = error_position; a_memory_region_number region_to_switch_back_to; switch_to_file_scope_region(®ion_to_switch_back_to); if (ctws_state == NULL) { init_ctws_state(&new_ctws_state); ctws_state = &new_ctws_state; } /* if */ if (expr != constraint && expr->kind == enk_constant) { /* If the expression wraps a constant from a partially-substituted constraint, use that constant for the final substitution. */ allocated_cp = copy_template_param_con(expr->variant.constant.ptr, template_arg_list, template_param_list, (a_type_ptr)NULL, &expr->position, options, ©_error, ctws_state, cp); expr = NULL; } else { expr = copy_template_param_expr(expr, template_arg_list, template_param_list, (a_type_ptr)NULL, &expr->position, options, ©_error, ctws_state, cp, &allocated_cp); } /* if */ if (copy_error) { constraint_result = a_test_constraint_result::subst_failed; } /* if */ switch_back_to_original_region(region_to_switch_back_to); error_position = saved_err_pos; } /* if */ } /* if */ if (!copy_error && constraint_result == a_test_constraint_result::pending && expr != NULL) { a_type_ptr ctp = strip_implicit_operations(expr)->type; if (!is_bool_type(ctp)) { /* If the type is not a boolean after substitution, the failure is not SFINAE-like. */ *p_fatal = !is_error_type(ctp); constraint_result = a_test_constraint_result::nonbool_result; } else { if (is_glvalue_node(expr)) { /* Just setting force_prvalue in the call to interpret_expr below is not sufficient because creating a prvalue may require some instantiations not triggered by the call to interpret_expr. So we explicitly do the conversion first. */ expr = conv_glvalue_expr_to_prvalue(expr, (a_boolean *)NULL, (a_constant_ptr *)NULL, (a_source_position*)NULL); } /* if */ if (!interpret_expr(expr, /*is_constant_evaluated=*/TRUE, /*force_prvalue=*/TRUE, cp, &interpret_diag_list)) { *p_fatal = TRUE; constraint_result = a_test_constraint_result::eval_failed; } /* if */ } /* if */ } /* if */ if (!copy_error && constraint_result == a_test_constraint_result::pending) { if (allocated_cp == NULL) { allocated_cp = cp; } /* if */ if (!is_bool_type(allocated_cp->type)) { /* If the type is not a boolean after substitution, the failure is not SFINAE-like. */ *p_fatal = !is_error_type(allocated_cp->type); constraint_result = a_test_constraint_result::nonbool_result; } else { if (!constant_is(allocated_cp, ck_template_param) && !is_false_constant(allocated_cp)) { constraint_result = a_test_constraint_result::satisfied; } else { constraint_result = a_test_constraint_result::not_satisfied; } /* if */ } /* if */ } /* if */ if (constraint_result != cached_result && !notes_pass) { /* Update the cached result. */ (void)constraint_satisfaction_cache->map_or_replace_with_hash( test, constraint_result, hash); } /* if */ defer_instantiations--; if (defer_instantiations == 0) { process_deferred_instantiation_requests(); } /* if */ } else { constraint_result = a_test_constraint_result::subst_failed; } /* if */ switch (constraint_result) { case a_test_constraint_result::pending: pos_error(ec_circular_constraint, &constraint->position); more_info_diagnostic(ec_atomic_constraint_substitution_failed, &constraint->position, diag_list); result = FALSE; *p_fatal = TRUE; break; case a_test_constraint_result::subst_failed: more_info_diagnostic(ec_atomic_constraint_substitution_failed, &constraint->position, diag_list); result = FALSE; break; case a_test_constraint_result::eval_failed: more_info_diagnostic(ec_atomic_constraint_evaluation_failed, &constraint->position, diag_list); /* Append the failure notes produced by the interpreter. */ splice_diag_list(&interpret_diag_list, diag_list, diag_list->tail); result = FALSE; break; case a_test_constraint_result::nonbool_result: more_info_diagnostic(ec_nonbool_atomic_constraint, &constraint->position, diag_list); result = FALSE; break; case a_test_constraint_result::not_satisfied: more_info_diagnostic(ec_atomic_constraint_false, &constraint->position, diag_list); result = FALSE; break; case a_test_constraint_result::satisfied: result = TRUE; break; case a_test_constraint_result::none: default: unexpected_condition_str2("constraint_satisfied_full:", "unexpected result"); } /* switch */ if (!result && notes_pass) { an_ovl_res_descr_ptr ovl_descr = ovl_stack->top(); a_diag_list *notes = &ovl_descr->notes; if (!ovl_descr->constraint_failure) { /* This is the first constraint failure reported for the current candidate. Record an introductory note. */ a_symbol_ptr cand = ovl_descr->curr_candidate; more_info_sym_tap_diagnostic(ec_candidate_constraints_failed, &cand->decl_position, cand, copy_template_arg_list( ovl_descr->curr_template_args), notes); ovl_descr->constraint_failure = TRUE; } /* if */ /* Additional "bottom-up" notes (i.e., notes indicating which concepts led to the constraint failure, if any) should be spliced before the constraint failure notes about to be appended to diag_list. */ ovl_descr->curr_diagnostic = notes->tail; splice_diag_list(diag_list, notes, notes->tail); clear_diag_list(diag_list); } /* if */ release_local_constant(&cp); } /* if */ if (!result && *p_fatal && diagnose_here) { /* A non-SFINAE error occurred, and the caller will not emit the associated diagnostic. So diagnose the issue here. */ error_with_notes(ec_template_constraint_not_satisfied, &error_position, diag_list); } /* if */ if (p_copy_error != NULL) *p_copy_error = copy_error; return result; } /* constraint_satisfied_full */ a_boolean constraint_satisfied(an_expr_node_ptr constraint, a_template_arg_ptr template_arg_list, a_template_param_ptr template_param_list, a_diag_list_ptr diag_list, /* Defaulted: */ a_ctws_options_set options, a_ctws_state_ptr ctws_state, a_boolean *p_fatal, a_boolean *p_copy_error) /* Convenience function to call constraint_satisfied_full with a single substitution pair. */ { a_subst_pairs_array subst_pairs(1); subst_pairs.push_back({ template_param_list, template_arg_list, FALSE, FALSE, FALSE, FALSE }); return constraint_satisfied_full(constraint, subst_pairs, diag_list, options, ctws_state, p_fatal, p_copy_error); } /* constraint_satisfied */ an_expr_node_ptr substitute_expr(an_expr_node_ptr expr, a_subst_pairs_array const &subst_pairs, a_ctws_state *ctws_state, a_ctws_options_set options, a_constant_ptr cp, a_constant_ptr *p_allocated_cp, a_boolean *p_err) /* Substitute the given expression with all the template parameter/argument pairs in subst_pairs (if there are multiple pairs in the array, the first ones represent enclosing class template instances). ctws_state is the substitution state to use throughout this process and the flags in options are passed along to the substitution routines. If the result of the substitution is a general expression, return its representation. If it is a constant that is already allocated in the IL, return it through *p_allocated_cp. If it is another constant value, record that value in *cp. If the substitution fails at any stage, set *p_err to TRUE. */ { a_constant_ptr allocated_cp = NULL; size_t levels = subst_pairs.length(); a_source_position *pos = &expr->position; a_boolean is_top_level_nonreal = scope_stack_top().in_nonreal_instantiation; for (size_t k = levels; k > 0 && !*p_err; --k) { a_subst_pairs_descr const *spd = &subst_pairs[k - 1]; a_ctws_options_set all_options = options | CTWS_NON_CONSTANT_EXPR; if (k > 1) { /* The next iteration may have to rescan the result. */ all_options |= CTWS_MAY_BE_RESCANNED; if (spd->adjust_coordinates) all_options |= CTWS_ADJUST_COORDINATES; if (spd->alias_deduction_guide) all_options |=CTWS_ALIAS_DEDUCTION_GUIDE; if (!is_top_level_nonreal) { /* Substitutions below the top level are always non-real. */ scope_stack_top().in_nonreal_instantiation = TRUE; } /* if */ } else { scope_stack_top().in_nonreal_instantiation = is_top_level_nonreal; } /* if */ if (expr != NULL) { expr = copy_template_param_expr( expr, spd->args, spd->params, (a_type_ptr)NULL, pos, all_options, p_err, ctws_state, cp, &allocated_cp); } else if (allocated_cp != NULL) { allocated_cp = copy_template_param_con( allocated_cp, spd->args, spd->params, (a_type_ptr)NULL, pos, all_options, p_err, ctws_state, cp); } else { a_constant_ptr src_cp = local_constant(); *src_cp = *cp; allocated_cp = copy_template_param_con( src_cp, spd->args, spd->params, (a_type_ptr)NULL, pos, all_options, p_err, ctws_state, cp); release_local_constant(&src_cp); } /* if */ } /* for */ *p_allocated_cp = allocated_cp; return expr; } /* substitute_expr */ static a_type_ptr check_requirement_expr( an_expr_node_ptr req_expr, a_subst_pairs_array const &subst_pairs, a_ctws_state *ctws_state, a_boolean constrained, a_boolean *p_is_noexcept) /* Perform the substitutions indicated by subst_pairs and ctws_state on req_expr. If that substitution is invalid, return NULL. Otherwise, return the type of the substituted expression or, if constrained is TRUE, the type produced by decltype((E)) where E is the substituted expression. Set *p_is_noexcept to TRUE, unless the substitution was successful and the resulting expression is potentially throwing. */ { a_type_ptr result = NULL; a_boolean err = FALSE, is_noexcept = TRUE; a_constant_ptr cp = local_constant(), allocated_cp = NULL; an_expr_node_ptr expr = req_expr; expr = substitute_expr(expr, subst_pairs, ctws_state, CTWS_COPY_ARG_OPERAND_INFO, cp, &allocated_cp, &err); if (err) { result = NULL; } else if (expr != NULL) { result = expr->type; if (expr_might_throw(expr)) { is_noexcept = FALSE; } /* if */ if (constrained) { if (expr->is_lvalue) { result = make_reference_type(result); } else if (expr->is_xvalue) { result = make_rvalue_reference_type(result); } /* if */ } /* if */ } else { if (allocated_cp == NULL) { allocated_cp = cp; } /* if */ result = allocated_cp->type; } /* if */ if (expr != NULL && expr != req_expr) { reclaim_fs_nodes_of_expr_tree(expr); } /* if */ release_local_constant(&cp); *p_is_noexcept = is_noexcept; return result; } /* check_requirement_expr */ a_boolean check_type_constraint(a_type_ptr type, an_expr_node_ptr constraint, a_subst_pairs_array const &subst_pairs, a_ctws_state *ctws_state, /* Defaulted: */ a_diag_list *diag_list) /* constraint is an enk_concept_id node with a possibly-empty argument list . Let C be the associated concept and T the type represented by the given type. Return TRUE if, after successful substitution of (using the substitutions described by subst_pairs and ctws_state), C is satisfied. Return FALSE otherwise and, if diag_list is non-NULL (it's NULL by default), update *diag_list accordingly. */ { a_boolean result = TRUE, copy_error = FALSE; a_template_arg_ptr first_arg, new_args; a_template_ptr templ = constraint->variant.concept_id.concept_template; a_symbol_ptr sym = symbol_for(templ); a_template_param_ptr params = sym->variant.template_info->cache->decl_info-> parameters; a_boolean saved_in_concept_rescan; push_instantiation_scope_for_rescan(sym); /* Record that we're rescanning a concept. This limits the substitution to the concept parameters, instead of picking up enclosing template parameters from the context that triggered the concept-id substitution. */ saved_in_concept_rescan = scope_stack_top().in_concept_rescan; scope_stack_top().in_concept_rescan = TRUE; /* Substitute the concept-id arguments. */ first_arg = alloc_template_arg((a_templ_arg_kind)tak_type); first_arg->variant.type = type; first_arg->next = constraint->variant.concept_id.args; new_args = templ_args_after_substitutions(sym, first_arg, params, (a_template_param_ptr)NULL, subst_pairs, &constraint->position, CTWS_NO_OPTIONS, ©_error, ctws_state); if (copy_error) { result = FALSE; } else { an_expr_node_ptr expr = templ->prototype_instantiation.constraint; a_diag_list local_diag_list; if (diag_list == NULL) { clear_diag_list(&local_diag_list); diag_list = &local_diag_list; } /* if */ /* Evaluate the constraint. */ result = constraint_satisfied(expr, new_args, params, diag_list); } /* if */ if (!result) { an_ovl_res_descr_ptr descr = ovl_res_descr(); if (descr.has_value() && descr->emit_note_diagnostics && !subst_pairs.is_empty()) { /* Overload resolution failed and we are repeating constraint checking to collect diagnostic notes. */ a_diag_list new_note = { NULL, NULL }, *notes = &descr->notes; more_info_sym_tap_diagnostic(ec_concept_not_satisfied, &constraint->position, sym, copy_template_arg_list( copy_error ? first_arg : new_args), &new_note); splice_diag_list(&new_note, notes, descr->curr_diagnostic != NULL ? descr->curr_diagnostic : notes->tail); } /* if */ } /* if */ scope_stack_top().in_concept_rescan = saved_in_concept_rescan; pop_instantiation_scope_for_rescan(); return result; } /* check_type_constraint */ a_boolean requires_expr_satisfied_full(an_expr_node_ptr requires_expr, a_subst_pairs_array const &subst_pairs, a_ctws_state_ptr ctws_state) /* The given node is a requires-expression. Return TRUE if substituting the template arguments of subst_pairs for the corresponding parameters of subst_pairs is successful. ctws_state is a substitution state block pointer (primarily used for parameter pack information of enclosing functions). */ { a_boolean result = TRUE, copy_error = FALSE; an_expr_node_ptr req = requires_expr->variant.requires_expr.requirements; a_param_type_ptr ptp = requires_expr->variant.requires_expr.parameters; int32_t saved_routine_type_levels; a_variadic_param_info_ptr saved_variadic_param_info_tail; an_ovl_res_stack *ovl_stack = ovl_res_stack(); if (ovl_stack) { /* If this is called as part of overload resolution, push a new level of overload resolution so that diagnostics from failures in individual requirements are not recorded. This will likely change in the future, but requires a more sophisticated mechanism to order any recorded diagnostics. */ ovl_stack->push(); } /* if */ /* Adjust the levels of enclosing parameter pack entries. */ for (a_variadic_param_info_ptr vpip = ctws_state->variadic_param_info; vpip != NULL; vpip = vpip->next) { ++vpip->level; } /* for */ saved_variadic_param_info_tail = ctws_state->variadic_param_info_tail; saved_routine_type_levels = ctws_state->routine_type_levels; if (ptp != NULL && subst_pairs.length() != 0) { /* Check that the parameter list can successfully be substituted, and record parameter pack information in *ctws_state if needed. */ ctws_state->routine_type_levels = 0; (void)param_types_after_substitutions(ptp, subst_pairs, &requires_expr->position, CTWS_NO_OPTIONS, ©_error, ctws_state); if (copy_error) result = FALSE; } /* if */ if (result) { for (; req != NULL && result; req = req->next) { switch (req->kind) { case enk_type_operand: if (subst_pairs.length() != 0) { a_type_ptr tp = req->variant.type_operand.type; tp = type_after_substitutions(tp, subst_pairs, &req->position, CTWS_NO_OPTIONS, ©_error, ctws_state); if (copy_error) result = FALSE; } /* if */ break; case enk_compound_req: { an_expr_node_ptr req_expr = req->variant.compound_req.expr_and_constraint, req_constr = req_expr->next; a_boolean is_noexcept, constrained = req_constr != NULL; a_type_ptr expr_type; expr_type = check_requirement_expr(req_expr, subst_pairs, ctws_state, constrained, &is_noexcept); if (expr_type == NULL) { result = FALSE; } else if (req->variant.compound_req.is_noexcept && !is_noexcept) { result = FALSE; } else { /* Check the type constraint. */ if (constrained && !check_type_constraint(expr_type, req_constr, subst_pairs, ctws_state)) { result = FALSE; } /* if */ } /* if */ } break; case enk_nested_req: { an_expr_node_ptr expr = req->variant.nested_req.constraint; a_diag_list diag_list; size_t levels = subst_pairs.length(); clear_diag_list(&diag_list); if (levels != 0) { a_template_param_ptr templ_params; a_template_arg_ptr templ_args; a_source_position saved_error_pos = error_position; if (levels > 1) { /* Always substitute the expression in a dependent context for non-top-level substitution pairs. */ a_boolean is_top_level_nonreal = scope_stack_top().in_nonreal_instantiation; if (!is_top_level_nonreal) { scope_stack_top().in_nonreal_instantiation = TRUE; } /* if */ for (size_t k = levels; k > 0; --k) { a_subst_pairs_descr const *spd = &subst_pairs[k - 1]; a_ctws_options_set options = CTWS_MAY_BE_RESCANNED; if (spd->adjust_coordinates) { options |= CTWS_ADJUST_COORDINATES; } /* if */ if (spd->alias_deduction_guide) { options |= CTWS_ALIAS_DEDUCTION_GUIDE; } /* if */ expr = copy_expr_with_substitutions(expr, spd->args, spd->params, options, ©_error, ctws_state); if (copy_error) break; } /* for */ scope_stack_top().in_nonreal_instantiation = is_top_level_nonreal; if (copy_error) { result = FALSE; break; } /* if */ } /* if */ /* Check constraint satisfaction for the top-level substitution. */ error_position = req->position; templ_params = subst_pairs.front_elem().params; templ_args = subst_pairs.front_elem().args; result = constraint_satisfied( expr, templ_args, templ_params, &diag_list, CTWS_NO_OPTIONS, ctws_state); discard_more_info_list(&diag_list); error_position = saved_error_pos; } else { a_constant_ptr cp = local_constant(); if (interpret_expr(expr, /*is_constant_evaluated=*/TRUE, /*force_prvalue=*/TRUE, cp, &diag_list)) { result = !is_false_constant(cp); } else { error_with_notes(ec_expr_not_constant, &expr->position, &diag_list); } /* if */ release_local_constant(&cp); } /* if */ } break; default: if (subst_pairs.length() != 0) { a_boolean is_noexcept; if (check_requirement_expr(req, subst_pairs, ctws_state, /*constrained=*/FALSE, &is_noexcept) == NULL) { result = FALSE; } /* if */ } break; } /* switch */ } /* for */ } /* if */ if (ctws_state->variadic_param_info != NULL) { /* Free parameter pack information for this requires expression's parameter list. */ if (saved_variadic_param_info_tail != NULL) { free_list_of_variadic_param_info(saved_variadic_param_info_tail->next); saved_variadic_param_info_tail->next = NULL; /* Restore the levels of enclosing parameter pack entries. */ for (a_variadic_param_info_ptr vpip = ctws_state->variadic_param_info; vpip != NULL; vpip = vpip->next) { --vpip->level; } /* for */ } else { free_list_of_variadic_param_info(ctws_state->variadic_param_info); ctws_state->variadic_param_info = NULL; } /* if */ ctws_state->variadic_param_info_tail = saved_variadic_param_info_tail; } /* if */ ctws_state->routine_type_levels = saved_routine_type_levels; if (ovl_stack) { ovl_res_stack()->pop(); } /* if */ return result; } /* requires_expr_satisfied_full */ a_boolean requires_expr_satisfied(an_expr_node_ptr requires_expr, a_subst_pairs_array const &subst_pairs) /* Interface to requires_expr_satisfied_full for the case where a default substitution state block is used. */ { a_ctws_state ctws_state; init_ctws_state(&ctws_state); return requires_expr_satisfied_full(requires_expr, subst_pairs, &ctws_state); } /* requires_expr_satisfied */ STATIC_THREAD Ptr_map *unoptimized_conditional_returns; /* Cached results of calls to unoptimize_conditional_return. */ a_dynamic_init_ptr unoptimize_conditional_return(a_dynamic_init_ptr dip, a_routine_ptr rp, a_source_position *pos) /* dip represents a "return" operation for the given routine, where the return expression is a conditional expression returning a class-type prvalue. C++17 mandates that no extra copy be done for this operation, but some compilers (notably MSVC) do sometimes insert a copy constructor invocation when the return operation is constant-evaluated. For example: struct S { constexpr S(int *p): p(p) {} constexpr S(S const&): p(nullptr) {} constexpr ~S() { if (p != nullptr) *p = 1; } int *p; }; constexpr S g(int n) { return true ? S(&n) : S(&n); } static_assert(g(42).p == nullptr); In the example above, MSVC invokes the copy constructor to return the result of g(42), which clears the pointer p embedded in the result. If the applicable copy constructor is nontrivial and unambiguous and is paired with a nontrivial destructor, this function returns a copy of dip with a copy constructor invocation added on top. Otherwise, it just returns dip. This function caches its results. The IL potentially generated by this function is not part of the general IL tree, and therefore not subject to memory region constraints. */ { a_dynamic_init *orig_dip = dip; dip = unoptimized_conditional_returns->get(dip); if (dip == NULL) { /* This is the first call for the given dynamic init entry. See if copying the result would require a call to a nontrivial constructor. */ a_routine *cctor = NULL; a_type_ptr ftp = skip_typerefs(rp->type), tp = skip_typerefs(ftp->variant.routine.return_type); a_boolean bitwise_copy = FALSE, err = FALSE; check_assertion(is_immediate_class_type(tp)); if (type_has_nontrivial_destructor(tp)) { cctor = select_copy_constructor_full( tp, TQ_NONE, /*source_is_rvalue=*/TRUE, pos, tp, &bitwise_copy, /*record_ref=*/TRUE, /*evaluated=*/TRUE, /*allow_suppressed_ctor=*/FALSE, /*check_access=*/FALSE, &err); } /* if */ if (cctor != NULL && !bitwise_copy && !err) { /* A nontrivial copy constructor invocation is needed: Create an entry to represent it. The underlying evaluation (i.e., the original dynamic initialization) now requires a local temporary and, if applicable, local destruction: To model that we make a shallow copy of orig_dip and add a destructor to it if needed. */ an_expr_node *tmp; a_param_type_ptr ptp = function_type_params(cctor->type); dip = alloc_dynamic_init(orig_dip->kind); *dip = *orig_dip; add_dtor_to_dynamic_init(dip, tp, tp, pos); tmp = alloc_temp_init_node(tp, dip, /*is_lvalue=*/TRUE, /*is_explicit_cast=*/FALSE); tmp = add_reference_to_to_node(tmp); dip = alloc_dynamic_init(dik_constructor); dip->variant.constructor.ptr = cctor; dip->variant.constructor.args = tmp; check_assertion(ptp != NULL); if (ptp->next != NULL && ptp->next->has_default_arg) { /* Append default arguments. */ tmp->next = copy_default_arg_expr_list( cctor, ptp->next, /*inside_conditional_expression=*/FALSE, /*potentially_evaluated=*/TRUE, /*evaluated=*/FALSE); } /* if */ } else { dip = orig_dip; } /* if */ unoptimized_conditional_returns->map(orig_dip, dip); } /* if */ return dip; } /* unoptimize_conditional_return */ #if DEBUG unsigned long show_expr_space_used(void) /* Display and return the amount of space used for various expression tables. */ { unsigned long num, size, total, grand_total = 0; db_space_used_header("Expression table use:"); db_space_used_lost("arg operands", avail_arg_operands, num_arg_operands_allocated, an_arg_operand); db_space_used_lost("init components", avail_init_components, num_init_components_allocated, an_init_component); db_space_used_lost("arg match summary", avail_arg_match_summaries, num_arg_match_summaries_allocated, an_arg_match_summary); db_space_used_lost("candidate function", avail_candidate_functions, num_candidate_functions_allocated, a_candidate_function); db_space_used_lost("ref entry", avail_ref_entries, num_ref_entries_allocated, a_ref_entry); #if SEQUENCING_DIAGNOSTICS_ENABLED db_space_used_lost("seq point variable entry", avail_seq_pt_var_entries, num_seq_pt_var_entries_allocated, a_seq_pt_var_entry); db_space_used_lost("seq point info entry", avail_sequence_info_entries, num_sequence_info_entries_allocated, a_seq_pt_info_entry); #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ db_space_used_lost("dynamic init dtor fixup", avail_dynamic_init_dtor_fixups, num_dynamic_init_dtor_fixups_allocated, a_dynamic_init_dtor_fixup); db_space_used("expr rescan info entry", num_expr_rescan_info_entries_allocated, an_expr_rescan_info_entry); db_space_used_total(); return grand_total; } /* show_expr_space_used */ #endif /* DEBUG */ #if CHECKING && DEBUG void check_all_init_component_entries_freed(void) /* Check that every an_init_component (aka an_expr_list_elem) entry allocated was ultimately freed. */ { #if ABORT_ON_INIT_COMPONENT_LEAKAGE check_assertion_str(num_init_components_allocated == (num_init_components_freed + num_init_components_permanently_allocated), "Some allocated init-component entries were never freed"); #endif /* ABORT_ON_INIT_COMPONENT_LEAKAGE */ } /* check_all_init_component_entries_freed */ #endif /* CHECKING && DEBUG */ void exprutil_one_time_init(void) /* Do one-time initialization of variables related to expression processing. */ { /* Save variables from exprutil.h, exprutil.c, and overload.h that are needed for precompiled headers */ if (precompiled_header_processing_required) { STATIC_THREAD a_pch_saved_variable saved_vars[] = { pch_saved_var_array_elem(avail_ref_entries), pch_saved_var_array_elem(reduce_backing_expression_use), #if SEQUENCING_DIAGNOSTICS_ENABLED pch_saved_var_array_elem(avail_seq_pt_var_entries), pch_saved_var_array_elem(avail_sequence_info_entries), #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ pch_saved_var_array_elem(avail_arg_operands), pch_saved_var_array_elem(avail_init_components), pch_saved_var_array_elem(avail_dynamic_init_dtor_fixups), pch_saved_var_array_elem(avail_arg_match_summaries), pch_saved_var_array_elem(avail_candidate_functions), pch_saved_var_array_elem(constraint_charts), pch_saved_var_array_elem(unoptimized_conditional_returns), #if TARG_HAS_IEEE_FLOATING_POINT pch_saved_var_array_elem(nan_constant), pch_saved_var_array_elem(infinity_constant), #endif /* TARG_HAS_IEEE_FLOATING_POINT */ #if C99_IL_EXTENSIONS_SUPPORTED pch_saved_var_array_elem(imaginary_unit), #endif /* C99_IL_EXTENSIONS_SUPPORTED */ #if DEBUG pch_saved_var_array_elem(num_arg_operands_allocated), pch_saved_var_array_elem(num_init_components_allocated), pch_saved_var_array_elem(num_init_components_permanently_allocated), pch_saved_var_array_elem(num_init_components_freed), pch_saved_var_array_elem(num_expr_rescan_info_entries_allocated), pch_saved_var_array_elem(num_ref_entries_allocated), #if SEQUENCING_DIAGNOSTICS_ENABLED pch_saved_var_array_elem(num_seq_pt_var_entries_allocated), pch_saved_var_array_elem(num_sequence_info_entries_allocated), #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ pch_saved_var_array_elem(num_dynamic_init_dtor_fixups_allocated), pch_saved_var_array_elem(num_arg_match_summaries_allocated), pch_saved_var_array_elem(num_candidate_functions_allocated), #endif /* DEBUG */ pch_saved_var_array_terminating_elem() }; register_pch_saved_variables(saved_vars); } /* if */ /* Register variables that must be saved and restored when switching between translation units. */ register_trans_unit_variable(expr_stack); register_trans_unit_variable(curr_expr_ref_entries); register_trans_unit_variable(decltype_rescan_operand); register_trans_unit_variable(reduce_backing_expression_use); #if TARG_HAS_IEEE_FLOATING_POINT register_trans_unit_variable(nan_constant); register_trans_unit_variable(infinity_constant); #endif /* TARG_HAS_IEEE_FLOATING_POINT */ #if C99_IL_EXTENSIONS_SUPPORTED register_trans_unit_variable(imaginary_unit); #endif /* C99_IL_EXTENSIONS_SUPPORTED */ register_trans_unit_variable(pending_consteval_failure); register_trans_unit_variable(requires_ranges); register_trans_unit_variable(requires_expr_substs); register_trans_unit_variable(cached_lambdas); #if SEQUENCING_DIAGNOSTICS_ENABLED sequencing_diagnostics_enabled = is_effective_diagnostic( ec_unsequenced_use_of_variable, es_remark, &error_position); #else /* !SEQUENCING_DIAGNOSTICS_ENABLED */ sequencing_diagnostics_enabled = FALSE; #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ } /* exprutil_one_time_init */ void exprutil_trans_unit_init(void) /* Initialize things related to expression scanning that must be re-initialized for each translation unit. */ { expr_stack = NULL; curr_expr_ref_entries = NULL; decltype_rescan_operand = NULL; reduce_backing_expression_use = FALSE; #if TARG_HAS_IEEE_FLOATING_POINT nan_constant = NULL; infinity_constant = NULL; #endif /* TARG_HAS_IEEE_FLOATING_POINT */ #if C99_IL_EXTENSIONS_SUPPORTED imaginary_unit = NULL; #endif /* C99_IL_EXTENSIONS_SUPPORTED */ internal_opnd_array = NULL; n_internal_opnds = 0; pending_consteval_failure.routine = NULL; requires_ranges = new_fe(/*mask_width=*/10u); requires_expr_substs = new_fe(/*mask_width=*/8u); cached_lambdas = new_fe(/*mask_width=*/5u); vars_being_deduced = new_fe>( /*mask_width=*/5u); } /* exprutil_trans_unit_init */ #if CHECKING static a_targ_size_t exponent_bits(a_float_kind kind) /* Return the number of bits required for the exponent of the specified kind of floating point number. */ { a_targ_size_t result = 0; int range = max_exponent[(int)kind] - min_exponent[(int)kind]; while (range > 0) { ++result; range >>= 1; } /* while */ return result; } /* exponent_bits */ #endif /* CHECKING */ void exprutil_init(void) /* Initialize things related to expression scanning that must be initialized for each compilation. */ { avail_ref_entries = NULL; #if SEQUENCING_DIAGNOSTICS_ENABLED avail_seq_pt_var_entries = NULL; avail_sequence_info_entries = NULL; #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ avail_arg_operands = NULL; avail_init_components = NULL; avail_dynamic_init_dtor_fixups = NULL; #if DEBUG num_arg_match_summaries_allocated = 0; num_arg_operands_allocated = 0; num_init_components_allocated = 0; num_init_components_permanently_allocated = 0; num_init_components_freed = 0; num_expr_rescan_info_entries_allocated = 0; num_ref_entries_allocated = 0; #if SEQUENCING_DIAGNOSTICS_ENABLED num_seq_pt_var_entries_allocated = 0; num_sequence_info_entries_allocated = 0; #endif /* SEQUENCING_DIAGNOSTICS_ENABLED */ num_dynamic_init_dtor_fixups_allocated = 0; #endif /* DEBUG */ constraint_charts = new_fe(/*mask_width=*/10u); template_param_objects = new_fe(/*mask_width=*/10u); unoptimized_conditional_returns = new_fe>(/*mask_width=*/10u); #if TARG_HAS_IEEE_FLOATING_POINT nan_constant = NULL; infinity_constant = NULL; #endif /* TARG_HAS_IEEE_FLOATING_POINT */ #if C99_IL_EXTENSIONS_SUPPORTED imaginary_unit = NULL; #endif /* C99_IL_EXTENSIONS_SUPPORTED */ constraint_satisfaction_cache = new_fe(/*mask_width=*/10u); /* Initialize floating point data. */ num_mantissa_bits[(int)fk_float16] = 11; num_mantissa_bits[(int)fk_fp16] = 11; num_mantissa_bits[(int)fk_float] = targ_flt_mant_dig; num_mantissa_bits[(int)fk_float32x] = targ_dbl_mant_dig; num_mantissa_bits[(int)fk_double] = targ_dbl_mant_dig; num_mantissa_bits[(int)fk_float64x] = targ_ldbl_mant_dig; num_mantissa_bits[(int)fk_long_double] = targ_ldbl_mant_dig; num_mantissa_bits[(int)fk_float80] = targ_flt80_mant_dig; num_mantissa_bits[(int)fk_float128] = targ_flt128_mant_dig; num_mantissa_bits[(int)fk_std_bfloat16] = 8; num_mantissa_bits[(int)fk_std_float16] = 11; num_mantissa_bits[(int)fk_std_float32] = 24; num_mantissa_bits[(int)fk_std_float64] = 53; num_mantissa_bits[(int)fk_std_float128] = 113; num_mantissa_bits[(int)fk_last] = 0; flt_type_size[(int)fk_float16] = 2; flt_type_size[(int)fk_fp16] = 2; flt_type_size[(int)fk_float] = targ_sizeof_float; flt_type_size[(int)fk_float32x] = targ_sizeof_double; flt_type_size[(int)fk_double] = targ_sizeof_double; flt_type_size[(int)fk_float64x] = targ_sizeof_long_double; flt_type_size[(int)fk_long_double] = targ_sizeof_long_double; flt_type_size[(int)fk_float80] = targ_sizeof_float80; flt_type_size[(int)fk_float128] = targ_sizeof_float128; flt_type_size[(int)fk_std_bfloat16] = 2; flt_type_size[(int)fk_std_float16] = 2; flt_type_size[(int)fk_std_float32] = 4; flt_type_size[(int)fk_std_float64] = 8; flt_type_size[(int)fk_std_float128] = 16; flt_type_size[(int)fk_last] = 0; min_exponent[(int)fk_float16] = -13; min_exponent[(int)fk_fp16] = -13; min_exponent[(int)fk_float] = targ_flt_min_exp; min_exponent[(int)fk_float32x] = targ_dbl_min_exp; min_exponent[(int)fk_double] = targ_dbl_min_exp; min_exponent[(int)fk_float64x] = targ_ldbl_min_exp; min_exponent[(int)fk_long_double] = targ_ldbl_min_exp; min_exponent[(int)fk_float80] = targ_flt80_min_exp; min_exponent[(int)fk_float128] = targ_flt128_min_exp; min_exponent[(int)fk_std_bfloat16] = -125; min_exponent[(int)fk_std_float16] = -13; min_exponent[(int)fk_std_float32] = -125; min_exponent[(int)fk_std_float64] = -1021; min_exponent[(int)fk_std_float128] = -16381; min_exponent[(int)fk_last] = 0; max_exponent[(int)fk_float16] = 16; max_exponent[(int)fk_fp16] = 16; max_exponent[(int)fk_float] = targ_flt_max_exp; max_exponent[(int)fk_float32x] = targ_dbl_max_exp; max_exponent[(int)fk_double] = targ_dbl_max_exp; max_exponent[(int)fk_float64x] = targ_ldbl_max_exp; max_exponent[(int)fk_long_double] = targ_ldbl_max_exp; max_exponent[(int)fk_float80] = targ_flt80_max_exp; max_exponent[(int)fk_float128] = targ_flt128_max_exp; /* Note that the following values are larger by one than the values given for "emax, maximum exponent" in P1467R8. Those values reflect the actual value of the exponent for the largest floating point number in the corresponding representation, while the front end convention follows that of the C17 standard in 5.2.4.2.2, "maximum integer such that FLT_RADIX raised to one less than that power is a representable finite floating-point number, e_max." */ max_exponent[(int)fk_std_bfloat16] = 128; max_exponent[(int)fk_std_float16] = 16; max_exponent[(int)fk_std_float32] = 128; max_exponent[(int)fk_std_float64] = 1024; max_exponent[(int)fk_std_float128] = 16384; max_exponent[(int)fk_last] = 0; #if CHECKING /* Ensure that the specified size for each type is large enough to hold the specified number of mantissa + exponent bits. */ for (int k = (int)fk_float16; k < (int)fk_last; ++k) { if (num_mantissa_bits[k] + exponent_bits((a_float_kind)k) > flt_type_size[k] * CHAR_BIT) { a_const_char *type_name = float_kind_name((a_float_kind)k, C_mode()); str_catastrophe(ec_bad_flt_config, type_name); } /* if */ } /* for */ #endif /* CHECKING */ /* Do initialization for overload.c: */ overload_init(); } /* exprutil_init */ /* Conditionally close the "edg" namespace. */ END_EDG_NAMESPACE