/* 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 */ /* expr.c -- Expression scanning 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 "disambig.h" #include "decl_spec.h" #include "declarator.h" #include "func_def.h" #include "interpret.h" #include "layout.h" #include "ifc_modules.h" #if MICROSOFT_EXTENSIONS_ALLOWED /* The Microsoft-specific predefined identifier __FUNCDNAME__ refers to the mangled name of the current function. Hence, we may need access to the mangling routines. */ #include "lower_name.h" #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #include "statements.h" /* Conditionally open the "edg" namespace. */ BEGIN_EDG_NAMESPACE /* Forward declarations. */ static void fix_up_dynamic_init_dtors(void); static a_boolean cast_type_pre_check( a_type_ptr *p_type_cast_to, a_source_position *type_position, a_boolean has_explicit_cv_qualifiers, a_boolean allow_array, a_boolean allow_unk_bound_array, a_boolean allow_incomplete_type = FALSE); static an_init_component_ptr parse_braced_init_list(a_boolean bundle); static void scan_braced_init_list_as_operand(an_operand *operand); static an_init_component_ptr scan_init_component_with_potential_pack_expansion( a_decl_parse_state *dps, a_boolean bundle, a_boolean parenthesized, a_boolean *expr_not_present); static void process_boolean_controlling_expression(an_operand *result); static void scan_compound_literal(a_type_ptr *p_literal_type, a_source_position *start_position, a_source_position *type_position, a_rescan_control_block *rcblock, an_init_component_ptr rescan_icp, an_operand *result, a_local_expr_options_set local_options); static void scan_expr_full(an_operand *result, an_operand *bound_function_selector, int prec_level, a_local_expr_options_set local_options); static an_arg_list_elem_ptr scan_expr_list( a_token_kind closing_token, a_boolean is_delegate_init, a_boolean is_custom_ms_attr_arg_list, a_boolean empty_list_okay, a_boolean trailing_comma_okay, a_boolean bundle); static void bound_function_in_cast(a_type_ptr type_cast_to, a_source_position *start_position, an_operand *operand, an_operand *bound_function_selector); static a_boolean var_declared_in_current_routine(a_variable_ptr var); static void make_param_ref_operand(an_operand *result, a_symbol_ptr param_sym); #if MICROSOFT_EXTENSIONS_ALLOWED static a_boolean process_runtime_checked_safe_cast( a_type_ptr type_cast_to, an_operand *operand, a_source_position *start_position, a_cast_source_form source_form); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ static void scan_yield_expression(an_operand *result); static void scan_await_expression(an_operand *result); static void make_zero_operand(an_operand *opnd, a_type_ptr tp); static void combine_ptr_to_member_operands( an_operand *operand_1, an_operand *operand_2, a_boolean is_arrow_operator, a_source_position *operator_position, an_operand *result, an_operand *bound_function_selector); /* Interface to scan_expr_full for the simple case where a bound function cannot be returned. */ #define scan_expr(result, prec_level, local_options) \ scan_expr_full((result), (an_operand *)NULL, (prec_level), \ (local_options)) a_boolean in_expression_context(void) /* Return TRUE if we are currently inside an expression context. */ { return (expr_stack != NULL); } /* in_expression_context */ a_boolean operand_is_instantiation_dependent(an_operand_ptr operand) /* Return TRUE if the given operand is instantiation-dependent, which normally includes type-dependent cases, value-dependent cases, and cases where a template parameter appears somewhere in a subexpression but doesn't make the overall result dependent in the other senses. */ { a_boolean contains_template_param = FALSE; a_constant_ptr con; if (is_expression_operand(operand) && expr_is_instantiation_dependent(operand->variant.expression)) { contains_template_param = TRUE; } else if (is_constant_operand(operand) && constant_is_instantiation_dependent(&operand->variant.constant)) { contains_template_param = TRUE; } else if (operand->symbol != NULL && is_template_dependent_indefinite_function(operand)) { contains_template_param = TRUE; } else if (is_braced_init_list_operand(operand)) { an_arg_list_elem_ptr alep = operand->variant.braced_init_list ->variant.braced.list; contains_template_param = arg_list_is_instantiation_dependent(alep); } else if (is_an_lvalue(operand) && (con = value_of_constant_var_lvalue_operand(operand)) != NULL) { /* A const variable with a dependent initializer is considered dependent. */ if (constant_is(con, ck_template_param)) { contains_template_param = TRUE; } /* if */ } /* if */ return contains_template_param; } /* operand_is_instantiation_dependent */ a_boolean arg_operand_is_instantiation_dependent( an_arg_operand_ptr arg_operand) /* Return TRUE if the given arg_operand is instantiation-dependent. */ { a_boolean contains_template_param = operand_is_instantiation_dependent( &arg_operand->operand); return contains_template_param; } /* arg_operand_is_instantiation_dependent */ a_boolean arg_list_is_instantiation_dependent(an_arg_list_elem_ptr alep) /* Return TRUE if the given list of argument elements contains an instantiation- dependent operand. */ { a_boolean result = FALSE; if (is_template_dependent_context()) { for (; alep != NULL; alep = next_elem(alep)) { if (is_expression_component(alep)) { an_operand *opnd = operand_of_arg_list_elem(alep); if (operand_is_instantiation_dependent(opnd)) { result = TRUE; break; } /* if */ } else if (is_braced_init_component(alep)) { if (arg_list_is_instantiation_dependent(alep->variant.braced.list)) { result = TRUE; break; } /* if */ } /* if */ } /* for */ } /* if */ return result; } /* arg_list_is_instantiation_dependent */ a_boolean arg_operand_involves_error_entity(an_arg_operand_ptr arg_operand) /* Return TRUE if the given arg_operand makes use of an error type or constant. */ { a_boolean contains_error = FALSE; an_operand *operand = &arg_operand->operand; if (is_error_operand(operand)) { contains_error = TRUE; } else if (is_expression_operand(operand) && expr_contains_error(operand->variant.expression)) { contains_error = TRUE; } else if (is_constant_operand(operand) && constant_contains_error(&operand->variant.constant)) { contains_error = TRUE; } else if (operand->is_template_id && template_arg_list_involves_error_entity( operand->template_arg_list)) { contains_error = TRUE; } /* if */ return contains_error; } /* arg_operand_involves_error_entity */ static void bundle_curr_expr_lifetime(an_arg_list_elem_ptr alep, a_boolean fix_up_dtors) /* alep represents a just-scanned operand (typically in the context of a front-end generated expression, such as for the handling of structured bindings or coroutines). Bundle the lifetime at the top of the current expression stack (if any) into *alep, and fix up the current expression context to disconnect it from *alep. If fix_up_dtors is TRUE, call fix_up_dynamic_init_dtors(). */ { an_object_lifetime_ptr lifetime = expr_stack->lifetime; if (fix_up_dtors) { fix_up_dynamic_init_dtors(); } /* if */ if (is_expression_component(alep)) { an_operand *operand = operand_of_arg_list_elem(alep); if (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); alep->variant.expr.lifetime = expr_stack->lifetime; curr_object_lifetime = curr_object_lifetime->parent_lifetime; expr_stack->lifetime = NULL; detach_from_object_lifetime_tree(alep->variant.expr.lifetime); } /* if */ alep->bundled = TRUE; detach_ref_entries_from_curr_expr(operand); } /* if */ if (expr_stack->consteval_function_designator_seen) { alep->consteval_function_designator_seen = TRUE; } /* if */ } /* bundle_curr_expr_lifetime */ /* Data structure to retain the current state of scanning the "new" operator. */ typedef struct a_new_parse_state { a_source_position start_position; /* The starting position of the "new" expression. This may differ from the position of the "new" itself, e.g., "::new". */ a_source_position new_position; /* The position of the "new" operator itself. */ a_source_position type_position; /* The position of the "new" type declaration. */ a_source_position init_position; /* The position of the "new" initializer, if present. */ a_source_position end_new_init_position; /* The end position of the new initializer. Only set for template and array new-init cases. */ #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; /* The end position for the entire "new" expression, including type and initializer. */ #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ a_type_ptr new_type; a_type_ptr unqual_new_type; /* The type of the object being allocated. */ a_type_ptr base_new_type; a_type_ptr unqual_base_new_type; /* The underlying type of the object being allocated. This will be the same as "new_type" except when allocating an (possibly multi-dimensional) array of objects, in which case it will be the type that makes up the array. Also see "element_type". */ a_type_ptr ptr_new_type; /* The result type of the "new" operator. */ a_type_ptr element_type; /* The underlying type of the object being allocated. Similar to "base_new_type" except that only one array dimension is stripped away. */ a_class_symbol_supplement_ptr cssp; /* The supplemental information for the class being allocated, if the type being allocated is a class type. */ an_expr_node_ptr new_array_dimension; /* The expression for the major array bound of the "new" array. */ an_expr_node_ptr init_val_node; /* The "new" initializer expression. */ a_symbol_ptr operator_new_symbol; /* The resolved operator "new" symbol. */ a_symbol_ptr function_symbol; a_symbol_ptr proj_function_symbol; /* The symbol for the "new" function, obtained from the list of functions contained in "operator_new_symbol". */ a_symbol_ptr ctor_sym; /* The constructor of the class being allocated, if the type being allocated is a class type. */ a_routine_ptr new_routine; /* The "new" routine that will be called. */ a_routine_ptr delete_routine; /* The associated "delete" routine, if present. */ an_arg_list_elem_ptr arg_list; /* The argument list for the "new" function call, including the size argument. May also include an alignment argument. */ an_expr_node_ptr arg_expr_list; /* The argument expression list corresponding to the arguments in "arg_list". */ an_arg_list_elem_ptr alignment_alep; /* The alignment argument for the "new" operator. */ an_arg_list_elem_ptr braced_init_list; /* The argument list representing the braced initializer for "new", if one was provided. */ an_arg_list_elem_ptr init_raw_args; /* The argument list representing the parenthesized initializer for "new", if one was provided. */ an_arg_match_summary_ptr arg_match_list; /* The argument match summary list corresponding to the match between the provided argument list and the selected "new" function. */ a_dynamic_init_ptr dip; /* The dynamic initialization needed when an initializer was provided. */ a_dynamic_init_ptr dyn_init_to_free_storage; /* The dynamic initialization used to free storage if an exception was thrown before initialization completed. */ #if MICROSOFT_EXTENSIONS_ALLOWED an_expr_node_ptr cli_array_new_init_args; /* The expression form of "init_raw_args". Used to infer array bounds. */ an_operand gcnew_special_case_operand; /* A replacement operand for when a gcnew of System::String with a single argument of type String is made, which just passes through the argument without doing a gcnew. */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ a_token_kind operator_token; /* The kind of "new" that is being scanned. */ a_targ_size_t effective_num_of_elements; /* The effective number of array elements when taking into account all array dimensions. */ a_bit_field use_global_new:1; /* TRUE if the global "new" operator should be used. */ a_bit_field delete_ambiguous:1; /* TRUE if the "delete" operator corresponding to the "new" operator is ambiguous. */ a_bit_field needs_initialization:1; /* TRUE if the allocated object(s) need to be initialized. */ a_bit_field variable_size_array:1; /* TRUE if the allocated object is a variable size array. */ a_bit_field zero_initialization:1; /* TRUE if the initialization amounts to zero-initialization. */ a_bit_field has_new_initializer:1; /* TRUE if an initializer was provided to "new". */ a_bit_field has_alignment_arg:1; /* TRUE if an alignment argument was provided to "new". */ a_bit_field has_braced_initializer:1; /* TRUE if the new initializer took the form of a braced initializer list. */ a_bit_field parens_for_aggr_init:1; /* TRUE if the new initializer took the form of a parenthesized initializer list. */ a_bit_field placement_new:1; /* TRUE if the "new" expression has (a) placement argument(s). */ a_bit_field array_new:1; /* TRUE if using the array version of "new". */ a_bit_field array_size_is_deduced:1; /* TRUE if the array size needs to be deduced from the initializer. */ a_bit_field unknown_dependent_new:1; /* TRUE if, during prototype instantiation, it's not currently known which "new" operator should be used. */ a_bit_field template_case:1; /* TRUE if, during prototype instantiation, the type being allocated is a template-dependent type. */ a_bit_field nonreal_new_type:1; /* TRUE if, during prototype instantiation, the type being allocated is a non-real class. */ a_bit_field deducible_new_type:1; /* TRUE if the type of the "new" expression is deducible - i.e., "auto" or "decltype(auto)" was used for the type, or class template argument deduction is occurring. */ a_bit_field using_expr_cache:1; /* TRUE if the initializer expression has been pre- scanned and should therefore be retrieved from the initializer cache. */ a_bit_field empty_initializer:1; /* TRUE if the initializer expression is empty. */ a_bit_field parenthesized_type_id:1; /* TRUE if the type in the "new" expression is parenthesized. */ a_bit_field templ_init_scanned:1; /* TRUE if, during prototype instantiation, "init_raw_args" have been prepared for passage to a template-dependent constructor. */ a_bit_field err:1; /* TRUE if an error has occurred during the parsing of the "new" expression. */ a_bit_field type_err:1; /* TRUE if an error has occurred during the determination of the "new" expression's type. */ #if MICROSOFT_EXTENSIONS_ALLOWED a_bit_field is_gcnew:1; /* TRUE if this is the "gcnew" variant of "new". */ a_bit_field cli_array_new:1; /* TRUE if this is a "gcnew" of the C++/CLI array type. */ a_bit_field has_array_init:1; /* TRUE if the initializer is a CLI array initializer. */ a_bit_field gcnew_has_array_init:1; /* TRUE if the "gcnew" has an array initializer. */ a_bit_field is_gcnew_string_special_case:1; /* TRUE if this is the "gcnew" special case. See "gcnew_special_case_operand". */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } a_new_parse_state; #if NULL_POINTER_IS_ZERO #define clear_new_parse_state_ptrs(nps) #else /* !NULL_POINTER_IS_ZERO */ static void clear_new_parse_state_ptrs(a_new_parse_state *nps) /* Set all pointers in the "new" parse state to NULL. */ { nps->new_type = NULL; nps->unqual_new_type = NULL; nps->base_new_type = NULL; nps->unqual_base_new_type = NULL; nps->ptr_new_type = NULL; nps->element_type = NULL; nps->cssp = NULL; nps->new_array_dimension = NULL; nps->init_val_node = NULL; nps->operator_new_symbol = NULL; nps->function_symbol = NULL; nps->proj_function_symbol = NULL; nps->ctor_sym = NULL; nps->new_routine = NULL; nps->delete_routine = NULL; nps->arg_list = NULL; nps->arg_expr_list = NULL; nps->alignment_alep = NULL; nps->braced_init_list = NULL; nps->init_raw_args = NULL; nps->arg_match_list = NULL; nps->dip = NULL; nps->dyn_init_to_free_storage = NULL; #if MICROSOFT_EXTENSIONS_ALLOWED nps->cli_array_new_init_args = NULL; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* clear_new_parse_state_ptrs */ #endif /* NULL_POINTER_IS_ZERO */ /* Macro to initialize the "new parsing state" pointed to by the argument. */ #define clear_new_parse_state(nps) { \ memzero((char*)(nps), sizeof(a_new_parse_state)); \ clear_new_parse_state_ptrs(nps); \ } /* clear_new_parse_state */ static a_scope_ptr curr_function_scope(void) /* Return the current function scope. This is usually innermost_function_scope, but within the signature of a lambda, it is the function enclosing the lambda. */ { a_scope_ptr result = innermost_function_scope; a_scope_stack_entry_ptr ssep = &scope_stack_top(); while (result == NULL && ssep->inside_local_class) { /* We're in a local class: Check if it is a closure. We may have to repeat this if lambda expressions are nested. */ if (scope_is(ssep, sck_func_prototype)) ssep -= 1; if (scope_is(ssep, sck_class_struct_union) || scope_is(ssep, sck_class_reactivation)) { a_type_ptr class_type = ssep->assoc_type; if (class_type_supp(class_type)->is_lambda_closure_class) { ssep -= 1; if (ssep->depth_innermost_function_scope != NO_SCOPE_DEPTH) { result = scope_stack[ssep->depth_innermost_function_scope].il_scope; } /* if */ } else { break; } /* if */ } else { break; } /* if */ } /* if */ return result; } /* curr_function_scope */ static void set_deduced_return_type(a_type_ptr return_type, a_source_position *err_pos, a_routine_ptr rout) /* We're currently defining the given function with a deduced return type (a C++11 lambda body or a C++14 function with an auto/decltype(auto) return type), and we've encountered a return statement that implies the given return_type (the type is void for a return without an expression). Set the function return type, issuing an error if this return type conflicts with a previously- established type. This function is also called to set the deduced return type of a defaulted spaceship operator with an "auto" return type (in that case, the return type is not produced from a return statement and the function is not being defined). */ { a_type_ptr rout_type, curr_return_type; rout_type = skip_typerefs(rout->type); check_assertion(rout_type->kind == (a_type_kind)tk_routine); curr_return_type = rout_type->variant.routine.return_type; if (!rout->has_deduced_return_type) { /* The return type has not been established yet, so set it. */ if (check_return_type(return_type, (a_decl_parse_state*)NULL, err_pos)) { /* Type is okay. */ rout_type->variant.routine.return_type = return_type; set_routine_calling_method_flag(rout_type, err_pos); } else { return_type = error_type(); } /* if */ if (depth_innermost_function_scope != NO_SCOPE_DEPTH) { scope_stack[depth_innermost_function_scope].orig_return_type = curr_return_type; } /* if */ rout->has_deduced_return_type = TRUE; } else if (!identical_types(return_type, curr_return_type)) { /* Multiple returns with different types. That might be a problem. */ if (is_error_type(return_type) || is_error_type(curr_return_type)) { /* At least one of the types is an error type, so consider them compatible, and the return type is an error type. */ return_type = error_type(); } else if (is_template_dependent_context() && (is_template_dependent_type(return_type) || is_template_dependent_type(curr_return_type))) { /* At least one of the types is template-dependent, so assume they are compatible. The return type becomes the non-dependent type if we have one, otherwise an unknown dependent type. */ if (!is_template_dependent_type(curr_return_type)) { return_type = curr_return_type; } else if (!is_template_dependent_type(return_type)) { /* Leave return_type alone. */ } else { return_type = type_of_unknown_templ_param_nontype; } /* if */ } else { /* Two returns have different types. */ expr_pos_ty2_error(ec_deduced_return_type_conflict, err_pos, return_type, curr_return_type); return_type = error_type(); } /* if */ } /* if */ /* Put the return type back in case it was changed above. */ rout_type->variant.routine.return_type = return_type; } /* set_deduced_return_type */ static a_type_ptr decltype_from_operand(an_operand *operand, a_boolean *no_parens_matters); static a_boolean deduce_placeholder_type( a_boolean is_decltype_auto, a_boolean is_class_template, a_boolean is_direct_init, a_boolean parenthesized_init, a_boolean for_template_arg, a_type_ptr orig_type, a_type_ptr auto_type, a_boolean keep_placeholder, an_operand *initializer_operand, an_arg_list_elem *initializer_alep, a_source_position *source_pos, a_type_ptr *type_after_deduction, a_type_ptr *deduced_auto_type, a_boolean *still_dependent) /* Do type deduction for a use of "auto", "decltype(auto)", or for deduction of class template arguments in a declaration or similar construct. If the call is for a "decltype(auto)" construct, the flag is_decltype_auto is TRUE. If the call is for deduction of class template arguments, is_class_template is TRUE. If this deduction is from a direct initializer, is_direct_init is TRUE (and is_parenthesized_init is TRUE if the initializer is parenthesized). for_template_arg is TRUE for a placeholder for a nontype template argument. orig_type is the type of the declared entity, with "auto" embedded in it. auto_type is the "auto" type that's embedded (a template parameter type); it can be NULL, in which case this routine will find it inside orig_type. initializer_operand is the initializer, whose type is used to do the deduction. Alternatively, initializer_alep can be used to specify the initializer in init-component form; if it's non-NULL it is used instead of initializer_operand. source_pos is the source position of the declaration or NULL if called for deduction (e.g., of a nontype template parameter type). If the deduction succeeds, *type_after_deduction is set to the deduced version of orig_type, *deduced_auto_type is set to the type deduced for "auto" itself, and TRUE is returned. In that case, if keep_placeholder is TRUE, a tk_typeref is added on top of *deduced_auto_type. If an error is detected, FALSE is returned (but no diagnostic is issued). If the deduction was not attempted because the types involved are still dependent, *still_dependent is returned TRUE and FALSE is returned. */ { a_boolean result; an_expr_stack_entry *saved_expr_stack; an_expr_stack_entry expr_stack_entry; save_expr_stack(&saved_expr_stack); 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; expr_stack_entry.consteval_call_need_not_fold = for_template_arg && constexpr_enabled; if (scope_stack_top().is_rescan || source_pos == NULL || (is_prototype_instantiation_context() && (microsoft_mode || gpp_version_is(any_version)))) { /* During function template deductions, do not issue diagnostics. MSVC and GCC appear to also not diagnose failures when deducing placeholders in templates, even when the types involved are not dependent. */ expr_stack_entry.suppress_diagnostics = TRUE; } /* if */ if (is_class_template) { /* This is a class template argument deduction case. */ a_boolean allocated_alep = FALSE; *type_after_deduction = void_type(); *still_dependent = FALSE; if (initializer_operand != NULL) { check_assertion(initializer_alep == NULL); if (is_braced_init_list_operand(initializer_operand)) { /* Use the braced-init-list already attached to the operand. */ initializer_alep = initializer_operand->variant.braced_init_list; } else { initializer_alep =alloc_arg_list_elem_for_operand(initializer_operand); allocated_alep = TRUE; } /* if */ } /* if */ result = deduce_class_template_args(orig_type, is_direct_init, parenthesized_init, keep_placeholder, initializer_alep, source_pos, deduced_auto_type, still_dependent); if (allocated_alep) { free_arg_list(initializer_alep); } /* if */ if (result) { *type_after_deduction = *deduced_auto_type; } /* if */ } else if (!is_decltype_auto) { result = deduce_auto_type(orig_type, auto_type, keep_placeholder, initializer_operand, initializer_alep, source_pos, type_after_deduction, deduced_auto_type, still_dependent); } else { /* decltype(auto) succeeds unless the initializer is a braced initializer list. */ result = !((initializer_alep != NULL && is_braced_init_component(initializer_alep)) || (initializer_operand != NULL && is_braced_init_list_operand(initializer_operand))); if (result) { a_boolean no_parens_matters; an_operand *p_operand; if (initializer_alep != NULL) { check_assertion(is_expression_component(initializer_alep)); p_operand = operand_of_arg_list_elem(initializer_alep); } else { p_operand = initializer_operand; } /* if */ if (p_operand->is_template_id && is_indefinite_function_operand(p_operand)) { convert_function_template_to_single_function_if_possible( p_operand, /*will_call=*/FALSE); } /* if */ *deduced_auto_type = decltype_from_operand(p_operand, &no_parens_matters); if (keep_placeholder) { a_type_ptr type = alloc_type((a_type_kind)tk_typeref); type->variant.typeref.type = *deduced_auto_type; type->variant.typeref.kind = trk_is_deduced_decltype_auto; *type_after_deduction = type; } else { *type_after_deduction = *deduced_auto_type; } /* if */ if (is_template_dependent_type(*type_after_deduction)) { *still_dependent = TRUE; result = FALSE; } else { *still_dependent = FALSE; } /* if */ } else { *still_dependent = FALSE; } /* if */ } /* if */ 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". */ an_operand *p_operand; an_expr_node *node; if (initializer_alep != NULL) { check_assertion(is_expression_component(initializer_alep)); p_operand = operand_of_arg_list_elem(initializer_alep); } else { p_operand = initializer_operand; check_assertion(p_operand != NULL); } /* if */ node = make_node_from_operand(p_operand); diag_invalid_consteval_func_in_expr(node); } /* if */ pop_expr_stack(); restore_expr_stack(saved_expr_stack); return result; } /* deduce_placeholder_type */ a_boolean arg_matches_auto_template_param( a_type_ptr param_type, a_constant_ptr constant, an_arg_operand_ptr arg_operand, a_type_ptr *p_deduced_type, a_source_position_ptr position, /* Defaulted: */ a_template_arg_ptr arg_list, a_template_param_ptr param_list) /* Return TRUE if the auto template parameter type specified by param_type matches the type of the argument specified by constant or arg_operand. Only one of constant or arg_operand must be non-NULL. If position is non-NULL, a diagnostic is issued if either the resulting type is invalid as a nontype parameter, or if an auto type cannot be deduced. If *p_deduced_type is not NULL, it is set to either the deduced type or an error type. param_list is the template parameter list that the auto template parameter is part of, and arg_list is the corresponding list of template arguments that have been processed so far. arg_list and param_list are NULL by default. */ { a_boolean result = FALSE; a_type_ptr bottom_type; a_type_ptr deduced_type = NULL; a_type_ptr deduced_auto_type = NULL; a_boolean still_dependent = FALSE; an_operand local_operand; an_operand_ptr p_operand; a_boolean is_direct_init; /* If a constant is supplied, create an operand from it. Otherwise, use the supplied operand. */ if (constant != NULL) { check_assertion(arg_operand == NULL); make_constant_operand(constant, &local_operand); if (constant->kind == ck_address && local_operand.state == os_prvalue && is_any_reference_type(local_operand.type)) { /* make_constant_operand turns reference ck_address entries into prvalue operands. Restore glvalueness. */ local_operand.type = type_pointed_to(local_operand.type); set_glvalue_operand_state(&local_operand); if (constant->expr != NULL && node_is(constant->expr, enk_variable)) { local_operand.is_id_expression = TRUE; local_operand.id_expression_was_parenthesized = constant->expr->is_parenthesized; } /* if */ } /* if */ p_operand = &local_operand; } else { check_assertion(arg_operand != NULL); p_operand = &arg_operand->operand; } /* if */ /* Attempt to deduce the auto type from the argument type. */ bottom_type = find_bottom_of_type(param_type); if (is_error_type(bottom_type)) { /* In error cases, the "auto" type might have been lost. Deduce an error type. */ result = TRUE; deduced_type = bottom_type; } else { a_boolean is_class_template = is_class_template_placeholder_type(bottom_type); check_assertion(is_class_template || is_auto_type(bottom_type)); /* A braced constant template argument is copy-list-initialized. */ is_direct_init = !is_braced_init_list_operand(p_operand); if (deduce_placeholder_type( bottom_type->variant.template_param.is_decltype_auto, is_class_template, is_direct_init, /*parenthesized_init=*/FALSE, /*for_template_arg=*/TRUE, param_type, bottom_type, /*keep_placeholder=*/FALSE, p_operand, (an_arg_list_elem_ptr)NULL, position, &deduced_type, &deduced_auto_type, &still_dependent)) { if (param_list != NULL && bottom_type->variant.template_param.extra_info->coordinates.depth != CLASS_TEMPLATE_PLACEHOLDER_NESTING_DEPTH && !template_param_constraint_satisfied(bottom_type, deduced_auto_type, arg_list, param_list, /*parent_type=*/(a_type*)NULL, position)) { /* The constraint was not satisfied. */ } else { /* The deduction succeeded. Make sure the resulting type is valid as a nontype template parameter. */ a_source_position *diag_pos = position; if (scope_stack_top().is_rescan) { /* Do not issue a diagnostic in rescanning contexts. */ diag_pos = NULL; } /* if */ if (check_nontype_template_param_type(&deduced_type, /*from_auto=*/TRUE, diag_pos)) { result = TRUE; } /* if */ } /* if */ } else { if (still_dependent) { /* If the type is still dependent, return the original type. */ result = TRUE; deduced_type = param_type; } else { if (position != NULL) { pos_ty2_error(ec_cannot_deduce_auto_templ_param, position, param_type, p_operand->type); } /* if */ } /* if */ } /* if */ } /* if */ if (p_deduced_type != NULL) { *p_deduced_type = result ? deduced_type : error_type(); } /* if */ return result; } /* arg_matches_auto_template_param */ static a_boolean ignore_braces_for_placeholder_deduction( a_decl_parse_state *dps, an_init_component_ptr icp, a_boolean parenthesized_init) /* Return TRUE if a declaration involving a placeholder type and using a braced initializer should ignore the braces for the purpose of deducing the placeholder type. The declaration is described by *dps and *icp is the initializer (it is parenthesized if parenthesized_init is TRUE). Direct-list-initialization with an "auto" or "decltype(auto)" placeholder type only permits a single braced element, and in that case the braces are ignored (rule introduced by the C++ standardization committee's paper N3922). For class template argument deduction, outer braces are ignored if they contain a a single element whose type is a specialization or a class derived from a specialization of the class template denoted by the placeholder. parenthesized_init is TRUE if the initializer is parenthesized, FALSE otherwise. Return TRUE if this is a case where single-element braced initializer lists can have their braces ignored. */ { a_boolean result = FALSE; if (icp != NULL && is_braced_init_component(icp)) { if (dps->has_deducible_class_templ_args) { a_type_ptr dtp = skip_typerefs_not_typedefs_or_type_operators(dps->type); icp = icp->variant.braced.list; if (!parenthesized_init && icp != NULL && icp->next == NULL && is_expression_component(icp) && is_class_template_placeholder_type(dtp)) { a_template_arg_ptr args; a_type_ptr operand_type = operand_of_arg_list_elem(icp)->type; a_symbol_ptr class_tmpl_sym = dtp->variant.template_param.extra_info ->constraint.class_template_symbol; result = is_or_derived_from_instance_of_class_template(operand_type, class_tmpl_sym, &args); } /* if */ } else if (dps->has_direct_initializer && (!(clang_mode ? clang_version < 30800 : gpp_mode ? gnu_version < 50000 : FALSE) || (microsoft_mode && microsoft_version >= 1900))) { result = TRUE; } /* if */ } /* if */ return result; } /* ignore_braces_for_placeholder_deduction */ void prescan_initializer_for_auto_type_deduction( a_decl_parse_state *dps, a_boolean parenthesized_init) /* Prescan an initializer and deduce the type of a placeholder type ("auto", "decltype(auto)", or a class template placeholder) from it. In some cases, dps->retrieve_initializer_from_cache can be TRUE to indicate that the initializer was previously cached (in dps->prescanned_initializer_cache). The operand resulting from the scan is recorded in *dps for later consumption. On return, dps->deduced_auto_type is the type to which the placeholder type was deduced, and dps->type is the type of the entity to initialize. dps->has_deduced_type (which must be TRUE on entry) is cleared to FALSE if there was a deduction error, and dps->deduced_auto_type is returned NULL if deduction was not done because the type or initializer is still dependent. The prescanned operand can later be accessed using set_up_initializer_rescan. The initializer is parenthesized if parenthesized_init is TRUE (and the opening parenthesis has already been swallowed); otherwise, it's "="-form or "{...}" form. */ { an_expr_stack_entry expr_stack_entry; an_expr_stack_entry *saved_expr_stack = NULL; an_expression_kind expr_kind = (an_expression_kind)ek_normal; an_init_component_ptr icp; a_type_ptr undeduced_type, deduced_auto_type; a_boolean still_dependent; a_boolean is_full_expr = !dps->is_new_expr_type && !dps->is_init_capture && dps->sym != NULL; a_decl_parse_state *saved_decl_parse_state = NULL; a_variable_ptr var = NULL; check_assertion(dps->has_deduced_type && dps->auto_type != NULL); if (dps->sym != NULL) { /* Update the type in the IL entry. */ if (symbol_is(dps->sym, sk_variable)) { var = dps->sym->variant.variable.ptr; vars_being_deduced->map(var, TRUE); } else if (symbol_is(dps->sym, sk_static_data_member)) { var = dps->sym->variant.static_data_member.variable; vars_being_deduced->map(var, TRUE); } else if (dps->is_init_capture) { /* No IL entry has been created yet: nothing to do. */ } else { unexpected_condition(); } /* if */ } /* if */ /* Usually an initializer is a full expression and we must push an entry on the expression stack. However, the initializer for a new-expression or a lambda-capture is not a full expression and a stack entry will already have been pushed in that case. */ if (is_full_expr) { if (dps->in_class_scope) { /* In-class initializers are only valid for static const data members of integral or enum type, for constexpr static data members of literal type, and for inline static data members. For non-constexpr inline static data members, scan a normal expression; otherwise, scan an integral constant expression. GNU and Microsoft modes allow other kinds of constant-expressions, but we do not permit the combination of those extensions with the "auto" type specifier. (See scan_member_constant_initializer_expression.) */ a_boolean is_non_constexpr_inline_sdm = FALSE; if (dps->sym != NULL && symbol_is(dps->sym, sk_static_data_member)) { if (var != NULL && var->is_inline && !var->is_constexpr) { is_non_constexpr_inline_sdm = TRUE; } /* if */ } /* if */ if (!is_non_constexpr_inline_sdm) { expr_kind = (an_expression_kind)ek_integral_constant; } /* if */ } /* if */ push_expr_stack_for_initializer(&expr_stack_entry, &saved_expr_stack, expr_kind, is_full_expr, dps, (an_init_state *)NULL); /* Record the parse state in the scope stack. It may be needed to set lambda parent entities and/or lambda discriminator values. */ saved_decl_parse_state = scope_stack_top().decl_parse_state; scope_stack_top().decl_parse_state = dps; if (var != NULL && var->is_constexpr) { expr_stack->consteval_call_need_not_fold = TRUE; } /* if */ } /* if */ /* Scan the expression and save it in an initializer cache so it can be scanned as the initializer later, and deduce the "auto" type it implies. */ if (parenthesized_init) { /* In the parenthesized case, the expression is syntactically part of an expression-list, even though in many cases there must be a single expression, which means potentially it is a pack expansion. */ dps->initializer_is_expr_list = TRUE; if (dps->has_deducible_class_templ_args) { /* In the case of a class template argument deduction, the parenthesized case really amounts to scanning call arguments. */ if (dps->retrieve_initializer_from_cache) { /* The expression list is already available (e.g., through expression rescanning for a function-style cast). */ icp = dps->prescanned_initializer_cache.first_init; dps->retrieve_initializer_from_cache = FALSE; clear_initializer_cache(&dps->prescanned_initializer_cache); } else { icp = scan_expr_list(tok_rparen, /*is_delegate_init=*/FALSE, /*is_custom_ms_attr_arg_list=*/FALSE, /*empty_list_okay=*/TRUE, /*trailing_comma_okay=*/FALSE, /*bundle=*/TRUE); } /* if */ } else { dps->initializer_is_single_expr = TRUE; icp = scan_init_component_with_potential_pack_expansion( dps, /*bundle=*/is_full_expr, /*parenthesized=*/TRUE, (a_boolean *)NULL); } /* if */ } else { /* In the non-parenthesized case, it's a simple expression or a braced-init-list. */ if (dps->retrieve_initializer_from_cache) { /* The expression list is already available (e.g., through expression rescanning for a function-style cast). */ icp = dps->prescanned_initializer_cache.first_init; dps->retrieve_initializer_from_cache = FALSE; clear_initializer_cache(&dps->prescanned_initializer_cache); } else { icp = scan_expr_or_braced_init_list(/*bundle=*/is_full_expr, /*always_allow_braced=*/FALSE); } /* if */ } /* if */ check_assertion(!anything_cached(&dps->prescanned_initializer_cache)); if (icp != NULL) { add_init_component_to_initializer_cache( icp, /*to_front=*/TRUE, &dps->prescanned_initializer_cache); } /* if */ /* Do type deduction. */ if (C_mode()) { /* GNU C has some simplified deduction rules. */ a_type_ptr deduced_type, auto_type = dps->auto_type; check_assertion((gcc_mode || c23_mode) && icp != NULL && is_expression_component(icp) && !parenthesized_init); if (skip_typerefs(dps->type) != dps->auto_type) { pos_st_error(ec_modified_auto_type, &dps->auto_pos, c_auto_specifier_spelling(dps)); deduced_type = error_type(); } else { an_operand *init_operand = operand_of_arg_list_elem(icp); /* The deduced type is the type of the initializer after the array and function decays and the glvalue to prvalue conversion, so that "auto p = f;" with f a function deduces a pointer to function and "auto i = ci;" with ci a const int deduces a plain int. */ deduced_type = do_implicit_type_transformations(init_operand->type, init_operand); deduced_type = skip_typerefs(deduced_type); #if GNU_EXTENSIONS_ALLOWED && GNU_VECTOR_TYPES_ALLOWED if (is_vector_type(deduced_type)) { eliminate_boolean_vector(&deduced_type); } /* if */ #endif /* GNU_EXTENSIONS_ALLOWED && GNU_VECTOR_TYPES_ALLOWED */ } /* if */ set_type_kind(auto_type, (a_type_kind)tk_typeref); auto_type->variant.typeref.type = deduced_type; auto_type->variant.typeref.kind = trk_is_deduced_auto; dps->deduced_auto_type = deduced_type; } else { /* Normal (i.e., C++-mode) deduction. */ undeduced_type = dps->declared_type; if ((dps->dso_flags & DSO_CONSTEXPR) != 0 && !dps->decltype_auto_specifier_seen && !is_const_qualified_type(undeduced_type)) { /* constexpr variables are implicitly const. */ undeduced_type = make_qualified_type(undeduced_type, (a_type_qualifier_set)TQ_CONST); } /* if */ if (ignore_braces_for_placeholder_deduction(dps, icp, parenthesized_init)) { an_init_component_ptr elem_icp = icp->variant.braced.list; if (parenthesized_init && !gpp_mode) { /* Something like "auto x( { 3 } );". This is malformed per N4660 [dcl.init]/1. Don't elide braces unless matching GCC/Clang. A similar diagnostic may be issued in prep_list_initializer. Care should be taken to avoid double diagnostics. */ expr_pos_diagnostic(es_discretionary_error, ec_braced_init_in_parens, init_component_pos(icp)); } else if (elem_icp == NULL || !is_last_elem(elem_icp)) { /* Not a single element: Issue a diagnostic and proceed with the braced list. */ pos_diagnostic(es_discretionary_error, ec_auto_direct_list_init_requires_singleton, init_component_pos(icp)); } else if (is_braced_init_component(elem_icp)) { /* Something like "auto x{ { 3 } };". A deduction error will be issued later. */ expect_error(); } else { /* A single brace-enclosed element: Proceed with just the element. */ icp = elem_icp; } /* if */ } /* if */ if (dps->is_struct_binding_decl && icp != NULL && is_expression_component(icp) && !type_is(dps->type, tk_pointer) && is_array_type(operand_of_arg_list_elem(icp)->type)) { /* For a declaration like "auto [x, y] = array;" where there is no ref-qualifier (i.e., no "&" or "&&" preceding the "["), the type deduction is different in that it deduces an actual array type instead of a pointer to the underlying element type. */ a_type_qualifier_set tqs = get_type_qualifiers(dps->type); a_type_ptr tp = operand_of_arg_list_elem(icp)->type; dps->deduced_auto_type = tp; tp = make_qualified_type(tp, tqs); dps->type = tp; dps->specifiers_type = tp; check_deduced_auto_type(dps); } else if (!deduce_placeholder_type(dps->decltype_auto_specifier_seen, dps->has_deducible_class_templ_args, (dps->has_direct_initializer || dps->init_state.direct_init), parenthesized_init, /*for_template_arg=*/FALSE, undeduced_type, dps->auto_type, dps->is_new_expr_type, (an_operand *)NULL, icp, &dps->declarator_pos, &dps->type, &deduced_auto_type, &still_dependent)) { if (still_dependent || ((gpp_version_is(any_version) || ms_version_is(any_version)) && scope_stack_top().in_prototype_instantiation && innermost_function_scope != NULL)) { /* Deduction was not done because the types are still dependent. GCC and Microsoft appear to not diagnose failed deductions in template definitions (until such templates are instantiated). */ dps->type = undeduced_type; dps->deduced_auto_type = unknown_type(); } else { /* Deduction failed. */ expr_pos_error(dps->has_deducible_class_templ_args ? ec_cannot_deduce_class_template_arguments : (an_error_code)(dps->decltype_auto_specifier_seen ? ec_cannot_deduce_decltype_auto_type : ec_cannot_deduce_auto_type), &dps->auto_pos); dps->specifiers_type = dps->deduced_auto_type = dps->type = error_type(); dps->has_deduced_type = FALSE; /* Do not clear dps->auto_type_specifier_seen or dps->decltype_auto_specifier_seen here, as there is a block of code in prep_list_initializer that checks for the braces-inside- parentheses issue that we issued an error for above. That code needs to know the initializer was scanned in the context of auto type deduction in order to avoid issuing a spurious error. */ } /* if */ } else { /* Deduction succeeded. */ if (dps->deduced_auto_type != NULL && !type_is(dps->deduced_auto_type, tk_unknown) && !identical_types(dps->deduced_auto_type, deduced_auto_type)) { /* This is a declaration with multiple declarators and the type deduced for a previous declarator is not consistent with the current deduction: Issue an error. */ expr_pos_ty2_error(ec_inconsistent_deduction_of_auto, &dps->declarator_pos, deduced_auto_type, dps->deduced_auto_type); } /* if */ /* Record the type deduced for the "auto" specifier. */ dps->deduced_auto_type = deduced_auto_type; if ((dps->dso_flags & DSO_CONSTEXPR) != 0 && dps->decltype_auto_specifier_seen && !is_const_qualified_type(dps->type)) { /* constexpr variables are implicitly const. */ dps->type = make_qualified_type(dps->type, (a_type_qualifier_set)TQ_CONST); } /* if */ /* Check that the actual (deduced) type of the declaration is applicable to the declared entity (in particular, this checks for compatibility with previous declarations of the same entity). */ check_assertion(dps->sym != NULL || dps->prev_type == NULL); check_deduced_auto_type(dps); } /* if */ } /* if */ if (var != NULL) { /* Update the type in the IL entry and restore the initializer kind. */ var->type = dps->type; vars_being_deduced->unmap(var); } /* if */ /* Pop the expression stack if needed. */ if (is_full_expr) { scope_stack_top().decl_parse_state = saved_decl_parse_state; pop_expr_stack_for_initializer(saved_expr_stack, is_full_expr, dps, (an_init_state *)NULL); } /* if */ } /* prescan_initializer_for_auto_type_deduction */ void scan_and_discard_init_component(a_decl_parse_state *dps) /* Scan and discard an initializer (an expression or a braced initializer list) associated with the declaration described by *dps (dps may be NULL if no variable or static data member is associated with the initializer). This may amount to simply discarding a prescanned initializer. This routine is called for error recovery purposes. If dps is non-NULL, dps->has_initializer is set to FALSE. */ { an_expr_stack_entry expr_stack_entry; an_expr_stack_entry *saved_expr_stack; an_object_lifetime *saved_curr_object_lifetime = curr_object_lifetime; an_init_component_ptr icp; if (curr_il_region_number == file_scope_region_number && curr_object_lifetime != NULL && !in_file_scope(curr_object_lifetime)) { curr_object_lifetime = il_header.primary_scope->lifetime; } /* if */ push_expr_stack_for_initializer(&expr_stack_entry, &saved_expr_stack, (an_expression_kind)ek_normal, /*is_full_expr=*/TRUE, dps, (an_init_state *)NULL); icp = scan_expr_or_braced_init_list(/*bundle=*/TRUE, /*always_allow_braced=*/FALSE); arg_list_will_not_be_used_because_of_error(icp); free_init_component_list(icp); discard_curr_expr_object_lifetime(); pop_expr_stack_for_initializer(saved_expr_stack, /*is_full_expr=*/TRUE, dps, (an_init_state *)NULL); if (dps != NULL) dps->has_initializer = FALSE; curr_object_lifetime = saved_curr_object_lifetime; } /* scan_and_discard_init_component */ static a_ref_entry_ptr merge_ref_lists(a_ref_entry_ptr list1, a_ref_entry_ptr list2) /* Return a pointer to a list of reference entries that is the concatenation of list1 and list2. The source lists and the destination list are linked on the next_operand_ref field. */ { a_ref_entry_ptr merged_list; if (list1 == NULL) { merged_list = list2; } else if (list2 == NULL) { merged_list = list1; } else { /* Find the end of list1 and add list2 there. */ merged_list = list1; while (list1->next_operand_ref != NULL) list1 = list1->next_operand_ref; list1->next_operand_ref = list2; } /* if */ return merged_list; } /* merge_ref_lists */ static void check_expression_for_nodiscard_warning(an_expr_node_ptr node) /* Check the expression (or any relevant children) for the presence of a function call or dynamic initialization and issue a warning if the routine or return type or temporary type have the "nodiscard" standard attribute applied. */ { an_error_code error_code = ec_no_error; a_const_char *reason = NULL; a_routine_ptr rp; an_attribute_ptr ap; node = skip_parens(node); for (;;) { if (is_operation_node(node)) { if (node_operator_is(node, eok_cast)) { if (is_void_type(node->type)) { /* This is an explicit cast to void: suppress the warning. */ break; } else { /* Skip this cast. */ node = node->variant.operation.operands; } /* if */ } else if (node_operator_is(node, eok_comma)) { /* Follow the right branch of the comma expression. */ node = node->variant.operation.operands->next; } else if (node_operator_is(node, eok_question)) { /* Recurse to follow the second and third branches of an eok_question operation. */ check_expression_for_nodiscard_warning( node->variant.operation.operands->next); check_expression_for_nodiscard_warning( node->variant.operation.operands->next->next); break; } else if (node_operator_is(node, eok_ref_indirect)) { /* Remove a top-level ref-indirect. */ node = node->variant.operation.operands; } else if (is_call_node(node)) { /* Some type of function call; see if the nodiscard attribute is applicable. */ /* Retrieve the type of the routine being called. */ a_type_ptr tp = type_of_call(node); /* For the nodiscard attribute, there are two cases: the routine can have the attribute attached to it, or the class/enum return type might have the attribute. In either case, an explicit cast to void suppresses these warnings. */ rp = routine_from_function_expr(node->variant.operation.operands); if (rp != NULL && rp->has_nodiscard_attribute && !is_void_type(rp->type->variant.routine.return_type)) { ap = attribute_string_literal_arg(ak_nodiscard, &rp->source_corresp); if (ap != NULL && is_ordinary_string_constant(ap->arguments->variant.constant)) { reason = ap->arguments->variant.constant->variant.string.value; error_code = ec_nodiscard_routine_with_reason; } else { error_code = ec_nodiscard_routine; } /* if */ } else if (is_function_type(tp)) { /* Look at the function's return type. */ if (type_has_nodiscard_attribute(return_type_of(tp), &reason)) { if (reason != NULL) { error_code = ec_nodiscard_return_type_with_reason; } else { error_code = ec_nodiscard_return_type; } /* if */ } /* if */ } /* if */ break; } else { /* Didn't find a call node -- no warning. */ break; } /* if */ } else if (node->kind == (an_expr_node_kind)enk_temp_init) { /* Initialization of a temporary whose type has nodiscard should elicit a warning. */ if (type_has_nodiscard_attribute(node->type, &reason)) { if (reason != NULL) { error_code = ec_nodiscard_object_type_with_reason; } else { error_code = ec_nodiscard_object_type; } /* if */ break; } else if (node->variant.init.dynamic_init->kind == (a_dynamic_init_kind)dik_constructor) { /* If the type doesn't have the nodiscard attribute but the selected constructor does, then also issue a warning. */ rp = node->variant.init.dynamic_init->variant.constructor.ptr; if (rp != NULL && rp->has_nodiscard_attribute) { ap = attribute_string_literal_arg(ak_nodiscard, &rp->source_corresp); if (ap != NULL && is_ordinary_string_constant(ap->arguments->variant.constant)) { reason = ap->arguments->variant.constant->variant.string.value; error_code = ec_nodiscard_constructor_with_reason; } else { error_code = ec_nodiscard_constructor; } /* if */ } /* if */ break; } else if (node->variant.init.dynamic_init->kind == (a_dynamic_init_kind)dik_expression || node->variant.init.dynamic_init->kind == (a_dynamic_init_kind)dik_class_result_via_ctor) { /* Follow an expression in a dynamic init. */ node = node->variant.init.dynamic_init->variant.expression; } else { break; } /* if */ } else { /* No expression. */ break; } /* if */ node = skip_parens(node); } /* for */ if (error_code != ec_no_error) { if (reason != NULL) { expr_pos_st_warning(error_code, &node->position, reason); } else { expr_pos_warning(error_code, &node->position); } /* if */ } /* if */ } /* check_expression_for_nodiscard_warning */ static void process_void_operand(an_operand *operand, a_boolean suppress_warning) /* Examine the operand given by *operand, which has been scanned as a void expression, and issue a warning if the operand has no effect. Lvalue-to-rvalue transformations are done if appropriate. Other transformations are done in all cases. Processing for the "nodiscard" standard attribute is also performed. If suppress_warning is TRUE, certain warnings are not issued. When this flag is FALSE, it may be changed below to be TRUE for certain kinds of expressions and/or contexts. */ { /* Do lvalue-to-rvalue transformations, etc. as appropriate. */ do_void_operand_transformations(operand, /*force_lvalue_to_rvalue=*/FALSE); #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && is_cli_ref_or_interface_class_type(operand->type) && is_expression_operand(operand)) { /* MSVC does not allow a dereference of a handle to a ref or interface class to be discarded. We're told MSVC has no way to represent that internally. An explicit cast to void can be added to make it acceptable. Also, a handle to a value class is okay. */ an_expr_node_ptr expr = operand->variant.expression; expr = skip_parens(expr); if (is_operation_node(expr) && node_operator_is(expr, eok_indirect)) { pos_error(ec_unused_dereference_of_ref_class, &operand->position); suppress_warning = TRUE; } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (suppress_warning) { /* The caller has requested that warnings be suppressed. */ } else if (expr_stack->is_type_operator_arg_expression) { /* decltype expressions are sometimes written to check SFINAE conditions, so do not warn inside them. */ suppress_warning = TRUE; } else if (!is_expression_operand(operand)) { /* An operand that is not an expression cannot have side effects. For error operands, assume that the original form might have had an effect, and suppress the warning. Likewise for template-dependent constants. */ if (is_error_operand(operand)) { suppress_warning = TRUE; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (operand->is_microsoft_noop) { /* No warning for __noop(...), which does nothing by definition. */ suppress_warning = TRUE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else if (is_constant_operand(operand)) { a_constant_ptr con = &operand->variant.constant; if (constant_is(con, ck_template_param)) { suppress_warning = TRUE; } else if (con->is_result_of_constexpr_call) { suppress_warning = TRUE; } /* if */ } /* if */ } else { /* For an expression, traverse the tree to see if it has side effects. */ a_boolean has_explicit_cast_to_void = FALSE; an_expr_node_ptr node = operand->variant.expression; node = skip_parens(node); while (is_operation_node(node)) { if (node_operator_is(node, eok_cast) && is_void_type(node->type)) { /* This is an explicit cast to void: suppress the warning. This is because we assume that a programmer who casts something to void is doing so for some good reason, and also because the macro for "assert" expands to (void)0 when NDEBUG is defined. */ suppress_warning = TRUE; has_explicit_cast_to_void = TRUE; break; } else if (node_operator_is(node, eok_comma)) { /* For a comma node, the check for side effects was already done on the first operand when it was scanned (and a warning issued if appropriate), so do not repeat that test. Just check the second operand. This allows use of a cast to void on any operand of a comma expression to suppress the warning, e.g., ((void)0, (void)0). */ /* If the first operand has side effects, the whole operation has side effects, so suppress the warning on the whole operation. */ if (node_has_side_effects(node->variant.operation.operands, &suppress_warning)) { suppress_warning = TRUE; break; } /* if */ /* Continue the loop on the second operand. */ node = node->variant.operation.operands->next; node = skip_parens(node); } else { /* Neither a cast to void nor a comma operator. */ break; } /* if */ } /* while */ /* See if the node has some effect. */ if (!suppress_warning && node_has_side_effects(node, &suppress_warning)) { suppress_warning = TRUE; } /* if */ if (!has_explicit_cast_to_void && expr_stack->nodiscard_expr_seen) { /* The expression has not been explicitly cast to void and somewhere in the expression there is a function call to which the nodiscard attribute applies. Look further to see if a warning is appropriate.*/ check_expression_for_nodiscard_warning(node); } /* if */ } /* if */ if (!suppress_warning && is_void_type(operand->type)) { /* Don't warn for void expressions. */ suppress_warning = TRUE; } /* if */ if (!suppress_warning) { /* Give a warning on an expression that has no effect. */ expr_pos_warning(ec_expr_has_no_effect, &operand->position); } /* if */ } /* process_void_operand */ static a_boolean token_ends_expr(a_token_kind token, int prec_level, a_local_expr_options_set local_options) /* Return TRUE if the given token is not part of an expression being scanned at precedence level prec_level. Conversely, return FALSE if the given token is an operator that binds tightly enough that it's part of the current expression. local_options is the set of options for the current expression (used to decide how a comma should be treated). */ { a_boolean done; a_boolean new_assoc; int new_prec = 0; done = FALSE; new_assoc = LEFT_ASSOC; /* More common case. */ switch (token) { case tok_plus_plus:/* Postfix increment. */ case tok_minus_minus:/* Postfix decrement. */ case tok_lbracket: /* Subscript. */ case tok_lparen: /* Routine call. */ case tok_period: /* Field selector ".". */ case tok_arrow: /* Field selector "->". */ new_prec = PREC_POSTFIX; break; case tok_arrow_star: /* ->* */ case tok_period_star: /* .* */ new_prec = PREC_PTR_TO_MEMBER; break; case tok_star: case tok_divide: case tok_remainder: new_prec = PREC_MULT_DIV; break; case tok_plus: case tok_minus: new_prec = PREC_PLUS_MINUS; break; case tok_shift_right: /* ">>" can be the end of a template argument list in some modes. E.g., A> in C++11 mode. It's not if there is a set of parentheses or the like inside the template argument expression, since the ">>" is then not top-level. */ if (right_shift_can_be_angle_brackets && expr_stack->is_template_arg_expression && expr_stack->nested_construct_depth == 0) { done = TRUE; } /* if */ FALLTHROUGH case tok_shift_left: new_prec = PREC_SHIFT; break; case tok_gt: /* ">" can be the end of a template argument list: A */ /* It's not if there is a set of parentheses or the like inside the template argument expression, since the ">" is then not top-level. */ if (expr_stack->is_template_arg_expression && expr_stack->nested_construct_depth == 0) { done = TRUE; } /* if */ /* Not the end of a template argument list, so fall into the normal case. */ FALLTHROUGH case tok_lt: case tok_le: case tok_ge: new_prec = PREC_RELATIONAL; break; case tok_eq: case tok_ne: new_prec = PREC_EQ_NE; break; case tok_spaceship: new_prec = PREC_SPACESHIP; break; case tok_gnu_min: case tok_gnu_max: new_prec = PREC_GNU_MIN_MAX; break; case tok_ampersand: new_prec = PREC_AND; break; case tok_excl_or: new_prec = PREC_EXCL_OR; break; case tok_or: new_prec = PREC_OR; break; case tok_and_and: new_prec = PREC_AND_AND; break; case tok_or_or: new_prec = PREC_OR_OR; break; case tok_quest_mark: new_prec = PREC_QUEST_MARK; new_assoc = RIGHT_ASSOC; break; case tok_assign: case tok_times_assign: case tok_divide_assign: case tok_remainder_assign: case tok_plus_assign: case tok_minus_assign: case tok_shift_left_assign: case tok_shift_right_assign: case tok_and_assign: case tok_excl_or_assign: case tok_or_assign: /* Note that assignment operators are recognized but not legal in constant expressions. This produces clearer error messages. */ new_prec = PREC_ASSIGNMENT; new_assoc = RIGHT_ASSOC; break; case tok_comma: /* Note that the comma operator is recognized but not legal in constant expressions. This produces clearer error messages. */ if (local_options & EOPT_DISALLOW_COMMA_OPERATOR) { /* The comma operator is not recognized here, because it means something else in this context (e.g., in a function argument list). */ done = TRUE; } else { new_prec = PREC_COMMA; } /* if */ break; default: /* Not an operator, or an operator only usable as a prefix operator (e.g., throw); the expression ends. */ done = TRUE; } /* switch */ if (done) { /* Nothing more to do. */ } else if ((local_options & EOPT_FOLD_EXPR_CONTEXT) != 0 && new_prec < PREC_CAST && next_token() == tok_ellipsis) { /* The next operator cannot be part of a cast-expression, and it is followed by an ellipsis. This must be the beginning of a C++17 fold expression: ( OP ... ) (EOPT_FOLD_EXPR_CONTEXT was passed in when the leading '(' was encountered.) */ done = TRUE; } else { /* See if the new operator precedence is such that the operator is not part of the current expression. This is the case if the new operator has lower precedence than prec_level, or if the precedences are equal and the operator is left-associative. End the expression if one of these conditions is met. */ if (new_prec < prec_level || (new_prec == prec_level && new_assoc == LEFT_ASSOC)) { done = TRUE; } /* if */ } /* if */ return done; } /* token_ends_expr */ /* Macro to record source positions in an_operand at the end of scanning an expression. result is the result operand. start_pos and end_pos give the beginning and ending source positions. end_pos is used only if EXTRA_SOURCE_POSITIONS_IN_IL is TRUE. This macro does not set the position in the underlying expression, if any (see set_operand_position). */ #if EXTRA_SOURCE_POSITIONS_IN_IL #define set_base_operand_position(result, start_pos, end_pos) \ { error_position = (result)->position = *(start_pos); \ curr_construct_end_position = (result)->end_position = *(end_pos); \ } #else /* !EXTRA_SOURCE_POSITIONS_IN_IL */ #define set_base_operand_position(result, start_pos, end_pos) \ { error_position = (result)->position = *(start_pos); } #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ static void f_set_operand_position(an_operand *result, a_source_position *start_pos, ARG_UNUSED a_source_position *end_pos, a_source_position *operator_pos) /* Record the source position in an_operand at the end of scanning an expression. result is the result operand. start_pos and end_pos give the beginning and ending source positions. operator_pos gives the operator position; the pointer can be NULL if there is no operator position. Global variables error_position and curr_construct_end_position are set appropriately. */ { set_base_operand_position(result, start_pos, end_pos); /* If the operand is an expression, record positions in the expression itself. */ set_operand_expr_position_if_expr(result, operator_pos); } /* f_set_operand_position */ /* Macro to record source positions in an_operand at the end of scanning an expression. result is the result operand. start_pos and end_pos give the beginning and ending source positions. operator_pos gives the operator position. Some of these are used only if EXTRA_SOURCE_POSITIONS_IN_IL is TRUE. */ #if EXTRA_SOURCE_POSITIONS_IN_IL #define set_operand_position(result, start_pos, end_pos, operator_pos) \ f_set_operand_position(result, start_pos, end_pos, operator_pos) #else /* !EXTRA_SOURCE_POSITIONS_IN_IL */ #define set_operand_position(result, start_pos, end_pos, operator_pos) \ f_set_operand_position(result, start_pos, NULL, operator_pos) #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ a_boolean is_overloadable_type_operand_full(an_operand *operand, a_boolean first_operand, a_boolean CFOO_guard) /* Return TRUE if the given operand has a type for which operator overloading should be considered. If first_operand is TRUE, this is the first operand of an operation (C++/CLI has some special rules for such operands). Also return TRUE for error operands (because they might be overloadable). When CFOO_guard is TRUE, this test is being done to decide about calling check_for_operator_overloading, so return TRUE for other cases that get handled there (e.g., operands that are template-dependent at other than the top level; that's used to send those to check_for_operator_overloading so partially-dependent cases don't have to be handled in the general code). */ { a_boolean is_overloadable = (first_operand ? is_overloadable_first_operand_type(operand->type) : is_overloadable_type(operand->type)) || is_error_operand(operand); #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && !is_overloadable) { if (operand->is_simple_string_literal && literal_type_convertible_to_cli_string(operand->type)) { /* In C++/CLI mode, a string literal can act like a System::String and overloading can be done on that. */ is_overloadable = TRUE; } else if (CFOO_guard && !first_operand && is_overloadable_handle_type(operand->type)) { /* In C++/CLI mode, a handle should go to check_for_operator_overloading because with a static conversion function it can be converted to a fundamental type that could make a built-in operator usable. A handle type on a first operand allows direct overloading and is handled above. */ is_overloadable = TRUE; } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (!is_overloadable && is_braced_init_list_operand(operand)) { /* A braced-init-list might have a class type and therefore is overloadable. */ is_overloadable = TRUE; } /* if */ if (!is_overloadable && CFOO_guard && is_template_dependent_context() && is_template_dependent_type(operand->type)) { is_overloadable = TRUE; } /* if */ return is_overloadable; } /* is_overloadable_type_operand_full */ static a_boolean is_overloadable_type_operand(an_operand *operand) /* Return TRUE if the given operand has a type for which operator overloading should be considered. More specifically, return TRUE if we'd like to go to check_for_operator_overloading to handle an operation on this operand, which for convenience includes cases where the operand is template-dependent at other than the first level. */ { a_boolean is_overloadable = is_overloadable_type_operand_full(operand, /*first_operand=*/FALSE, /*CFOO_guard=*/TRUE); return is_overloadable; } /* is_overloadable_type_operand */ static a_boolean is_overloadable_type_first_operand(an_operand *operand) /* Return TRUE if the given operand has a type for which operator overloading should be considered, specifically on the first operand of an operation (there are some special rules for that in C++/CLI mode). More specifically, return TRUE if we'd like to go to check_for_operator_overloading to handle an operation on this operand, which for convenience includes cases where the operand is template-dependent at other than the first level. */ { a_boolean is_overloadable = is_overloadable_type_operand_full(operand, /*first_operand=*/TRUE, /*CFOO_guard=*/TRUE); return is_overloadable; } /* is_overloadable_type_first_operand */ static a_boolean is_overloadable_type_arg_list(an_arg_list_elem_ptr arg_list) /* Return TRUE if any operand on the given argument list has a type that is overloadable. */ { a_boolean is_overloadable = FALSE; an_arg_list_elem_ptr alep; for (alep = arg_list; alep != NULL; alep = next_elem(alep)) { if (is_expression_component(alep) && is_overloadable_type_operand(operand_of_arg_list_elem(alep))) { is_overloadable = TRUE; break; } /* if */ } /* for */ return is_overloadable; } /* is_overloadable_type_arg_list */ static void set_pointer_operand_is_second_flag(an_operand *operand) /* The indicated operand is the result of a subscript or pointer-addition operation that has the operands in the unusual order of the integral operand first and the pointer operand second. Set the flag in the expression node to indicate that. */ { if (is_expression_operand(operand)) { an_expr_node_ptr expr = operand->variant.expression; if (!is_error_node(expr)) { check_assertion(is_operation_node(expr) && (node_operator_is(expr, eok_subscript) || node_operator_is(expr, eok_add) || node_operator_is(expr, eok_padd))); expr->variant.operation.pointer_operand_is_second = TRUE; } /* if */ } /* if */ } /* set_pointer_operand_is_second_flag */ #if MICROSOFT_EXTENSIONS_ALLOWED static a_boolean has_default_indexed_property(a_type_ptr type) /* If the given type is a class type that has a C++/CLI default indexed property, or a handle to such a type, return TRUE. */ { a_boolean has_def_idx_property = FALSE; if (is_handle_type(type)) type = type_pointed_to(type); type = skip_typerefs(type); if (is_immediate_managed_class_type(type)) { a_class_symbol_supplement_ptr cssp = symbol_supplement_for_class(type); complete_class_type_is_needed(type); if (cssp->default_indexed_properties != NULL) { has_def_idx_property = TRUE; } /* if */ } /* if */ return has_def_idx_property; } /* has_default_indexed_property */ static a_boolean property_ref_has_accessor_that_yields_subscriptable_object( an_operand *operand) /* Return TRUE if the Microsoft property reference given by "operand" has a "get" accessor that will allow the conversion of the property reference to an object that can then be subscripted. Note that in some cases the conversion is not done because the property has subscripting accessors that are supposed to win out. */ { a_boolean has_subscriptable_accessor = FALSE; a_symbol_ptr get_sym; /* Get the "get" function symbol. */ get_sym = get_property_accessor_symbol(operand->symbol, /*put=*/FALSE, /*must_be_present=*/FALSE, &operand->position); if (get_sym != NULL) { a_boolean class_case = FALSE; a_boolean some_function_has_params = FALSE; a_boolean declspec_property = symbol_is(operand->symbol, sk_field); an_overload_set_traversal_block ostblock; for (get_sym = set_up_overload_set_traversal_simple(get_sym, &ostblock); get_sym != NULL; get_sym = next_symbol_in_overload_set(&ostblock)) { a_type_ptr rout_type; a_symbol_ptr fund_sym = fundamental_symbol_of(get_sym); check_assertion(fund_sym->kind == (a_symbol_kind)sk_member_function); rout_type = routine_symbol_type(fund_sym); if (rout_type->variant.routine.extra_info->param_type_list == NULL) { a_type_ptr return_type = return_type_of(rout_type); if (is_pointer_type(return_type) && is_object_type(type_pointed_to(return_type))) { /* This function has zero arguments and returns a pointer to object type, so it can be used to get an "array" to be subscripted. */ has_subscriptable_accessor = TRUE; break; } else if (has_default_indexed_property(return_type) || (is_handle_type(return_type) && is_cli_array_type(type_pointed_to(return_type)))) { /* This function has zero arguments and returns a subscriptable class type or a handle to a CLI array type, so it can be used to get an "array" to be subscripted. */ has_subscriptable_accessor = TRUE; break; } else if (!some_function_has_params && is_class_struct_union_type(return_type)) { /* If the function returns a class that has an operator[] function, the returned object is subscriptable. */ if (opname_member_function_symbol((an_opname_kind)onk_subscript, skip_typerefs(return_type)) != NULL) { class_case = TRUE; if (declspec_property) { /* For declspec properties, we don't break because we want to look for accessors with parameters. */ } else { break; } /* if */ } /* if */ } /* if */ } else { /* Some function has parameters, which suppresses the class case for declspec properties. */ some_function_has_params = TRUE; } /* if */ } /* for */ /* The class case is accepted for a declspec property only if the property has no accessors that might be used for subscripting. */ if (!has_subscriptable_accessor && class_case && declspec_property && !some_function_has_params) { has_subscriptable_accessor = TRUE; } /* if */ } /* if */ return has_subscriptable_accessor; } /* property_ref_has_accessor_that_yields_subscriptable_object */ static void make_property_ref_operand(a_symbol_ptr property, an_operand *operand, a_boolean is_arrow_operator, an_operand *result) /* Make an operand for a Microsoft property reference. property is an sk_property_set, sk_field, or sk_static_data_member symbol associated with the property name. operand is the class object, or (if is_arrow_operator is TRUE) a handle to the class object. The property reference operand created is returned in *result. For a static property, operand is NULL if no object was specified. */ { check_assertion(operand != result); clear_operand((an_operand_kind)ok_property_ref, result); result->type = unknown_type(); result->symbol = property; set_glvalue_operand_state(result); if (operand != NULL) { conv_selector_to_object_pointer(operand, &is_arrow_operator); result->variant.property_ref.object = make_node_from_operand(operand); } else { /* The property is static and no object was specified. */ result->variant.property_ref.object = NULL; } /* if */ } /* make_property_ref_operand */ static void make_event_ref_operand(a_symbol_ptr event, an_operand *operand, a_boolean is_arrow_operator, an_operand *result) /* Make an operand for a Microsoft C++/CLI event reference. event is an sk_field or sk_static_data_member symbol associated with the event name. operand is the class object, or (if is_arrow_operator is TRUE) a handle to the class object. The event reference operand created is returned in *result. For a static event, operand is NULL if no object was specified. */ { check_assertion(operand != result); clear_operand((an_operand_kind)ok_event_ref, result); result->type = unknown_type(); result->symbol = event; set_glvalue_operand_state(result); if (operand != NULL) { conv_selector_to_object_pointer(operand, &is_arrow_operator); result->variant.event_ref.object = make_node_from_operand(operand); } else { /* The event is static and no object was specified. */ result->variant.event_ref.object = NULL; } /* if */ } /* make_event_ref_operand */ static void rewrite_class_with_default_indexed_property_as_property_ref( an_operand *operand) /* operand is the left operand of a subscripting operation. If it is an object of a C++/CLI ref class type that has a default indexed property, or a handle to such a type, rewrite it as a property reference so the subscripts can be applied to that. */ { if (cli_or_cx_enabled) { a_type_ptr type = operand->type; if (has_default_indexed_property(type)) { /* The class has a default indexed property, so rewrite the reference. */ a_boolean handle_case = is_handle_type(type); a_type_ptr class_type = handle_case ? type_pointed_to(type) : type; a_class_symbol_supplement_ptr cssp = symbol_supplement_for_class(class_type); an_operand new_operand; do_operand_transformations(operand, handle_case ? TOPT_NO_OPTIONS : TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION); make_property_ref_operand(cssp->default_indexed_properties, operand, handle_case, &new_operand); restore_operand_details(&new_operand, operand); copy_operand(&new_operand, operand); } /* if */ } /* if */ } /* rewrite_class_with_default_indexed_property_as_property_ref */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ static void reduce_singleton_subscript_list(an_arg_list_elem_ptr *p_list, an_operand *subscript, a_boolean cli_array, a_source_position *end_pos) /* If the given list *p_list of subscript arguments contains a single element, copy its operand to *subscript. Otherwise, issue an error. cli_array is TRUE if the subscripting is of a CLI array construct. end_pos is the end position of the subscript operation: That position is used for the diagnostic if *p_list is NULL. In all cases, the list pointed by *p_list is freed and *p_list is set to NULL. */ { if (is_single_elem(*p_list)) { if (!is_expression_component(*p_list)) { expr_pos_error(ec_braced_init_list_not_allowed, init_component_pos(*p_list)); make_error_operand(subscript); } else { copy_operand(operand_of_arg_list_elem(*p_list), subscript); } /* if */ } else { if (cli_array) { expr_pos_error(ec_comma_operator_in_cli_subscript, init_component_pos(*p_list)); } else { expr_pos_error(*p_list == NULL ? ec_exp_primary_expr : ec_too_many_array_bounds, *p_list == NULL ? end_pos : init_component_pos(next_elem(*p_list))); } /* if */ make_error_operand(subscript); } /* if */ free_arg_list(*p_list); *p_list = NULL; } /* reduce_singleton_subscript_list */ static void scan_function_call(an_operand *operand, an_operand *bound_function_selector, a_boolean is_multi_subscript, a_rescan_control_block *rcblock, an_operand *result, an_operand *p_subscript = NULL); static void scan_subscript_operator(an_operand *operand_1, a_boolean offsetof_case, a_rescan_control_block *rcblock, an_operand *result) /* Scan the array subscripting operator, i.e., pointer-expression [ integral-expression ] integral-expression [ pointer-expression ] *operand_1 is the left operand. The current token is the "[" operator. Scan the second operand and the "]", combine the two operands into an expression, and return an operand for that in *result. If rcblock is non-NULL, redo semantic analysis on a previously-scanned expression, and return the result in *result (or an error indication in *rcblock). operand_1 is expected to be NULL in that case. This routine is also used when scanning __builtin_offsetof constructs, in which case offsetof_case is TRUE. Starting with C++23, multiple subscripts are permitted (and the comma operator cannot be used at the top level). E.g., C++23 allows: struct S { int operator[](int, int); } s; int r = s[1, 2]; Those cases are mostly delegated to scan_function_call. */ { an_operand local_operand_1, operand_2; an_arg_list_elem_ptr operand_2_list = NULL; a_type_ptr result_type = NULL; a_source_position operator_position, closing_bracket_position; a_token_sequence_number operator_tok_seq_number; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ a_boolean err = FALSE, processed = FALSE, opnd2_done = FALSE; a_boolean subscript_is_expr_list = FALSE, cli_array_case = FALSE; db_enter(4, "scan_subscript_operator"); if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. */ check_assertion(rcblock->operator_token == tok_lbracket); check_assertion(operand_1 == NULL); operand_1 = &local_operand_1; check_assertion(is_operation_node(rcblock->expr)); if (node_operator_is(rcblock->expr, eok_subscript) && !multi_subscript_enabled) { make_rescan_operands(rcblock, operand_1, &operand_2, (an_operand *)NULL, &operator_position, &operator_tok_seq_number, &closing_bracket_position); } else { /* C++23 subscript or C++/CLI eok_cli_subscript case. */ /* Get the first operand, and then rescan the rest of the operands as a list of expressions. */ make_rescan_operands(rcblock, operand_1, (an_operand*)NULL, (an_operand*)NULL, &operator_position, &operator_tok_seq_number, &closing_bracket_position); operand_2_list = rescan_expr_list( rcblock->expr->variant.operation.operands->next, rcblock); if (!node_operator_is(rcblock->expr, eok_cli_subscript)) { /* A C++23 subscript operation. */ a_type *utp = skip_typerefs(operand_1->type); check_assertion(multi_subscript_enabled); if (!is_single_elem(operand_2_list) && (is_immediate_class_type(utp) || type_is(utp, tk_template_param))) { /* Delegate the C++23 multi-subscript case to scan_function_call. */ rcblock->operator_token = tok_lparen; scan_function_call((an_operand *)NULL, (an_operand *)NULL, /*is_multi_subscript=*/TRUE, rcblock, result); #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = result->end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ goto cleanup_and_done; } else { /* An ordinary "built-in" subscript. */ if (is_single_elem(operand_2_list)) { operand_2 = *operand_of_arg_list_elem(operand_2_list); } else { subscript_is_expr_list = TRUE; expr_pos_error(operand_2_list == NULL ? ec_exp_primary_expr : ec_too_many_array_bounds, operand_2_list == NULL ? &closing_bracket_position : init_component_pos(operand_2_list)); } /* if */ } /* if */ } else { /* C++/CLI eok_cli_subscript: */ cli_array_case = TRUE; subscript_is_expr_list = TRUE; } /* if */ } /* if */ #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = rcblock->expr->expr_range.end; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } else { /* Normal, non-rescan, processing. */ operator_position = pos_curr_token; operator_tok_seq_number = curr_token_sequence_number; if (std_attributes_enabled && next_token() == tok_lbracket) { /* '[ [' should strictly-speaking introduce a standard attribute, even though it could conceivably be a subscript operation with a lambda expression. */ pos_diagnostic(es_discretionary_error, ec_must_introduce_attribute, &pos_curr_token); } /* if */ if (multi_subscript_enabled) { /* Delegate the C++23 overloaded operator case to scan_function_call. If the delegation is successful, operand_2 will be left unchanged (i.e., it will still be an error operand). Otherwise, it operand_2 will be the single subscript to handle in the traditional way. */ a_type *utp = skip_typerefs(operand_1->type); if (is_immediate_class_type(utp) || type_is(utp, tk_template_param)) { an_operand bound_function_selector; copy_operand(operand_1, &local_operand_1); clear_operand(ok_error, &operand_2); scan_function_call(&local_operand_1, &bound_function_selector, /*is_multi_subscript=*/TRUE, rcblock, result, &operand_2); if (is_error_operand(&operand_2)) { /* scan_function_call fully handled the subscript construct. */ goto done; } /* if */ closing_bracket_position = pos_curr_token; add_matching_stop_token(tok_rbracket); opnd2_done = TRUE; } /* if */ } /* if */ } /* if */ if (curr_expr_kind_is_traditional_const()) { if (curr_expr_kind_is(ek_pp)) { /* Subscripting not allowed in preprocessing expression. */ expr_pos_error(ec_bad_pp_operator, &operator_position); err = TRUE; } else if (curr_expr_kind_is(ek_integral_constant)) { /* Subscripting not allowed in integral constant expression. */ expr_pos_error(ec_bad_integral_operator, &operator_position); err = TRUE; } else if (curr_expr_kind_is(ek_template_arg)) { /* Subscripting not allowed in a template argument expression. */ expr_pos_error(ec_bad_templ_arg_expr_operator, &operator_position); err = TRUE; } /* if */ } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (ms_extensions) { if (cli_or_cx_enabled) { a_type_ptr op1_type = operand_1->type; cli_array_case = is_handle_to_cli_array_type(op1_type); if (!cli_array_case) { /* cli::array has an inherited default-indexed property, but it shouldn't be used since cli::array is intrinsically indexable. */ rewrite_class_with_default_indexed_property_as_property_ref(operand_1); } /* if */ /* Decide on scanning what's between the [ ... ] as an expression list or a single expression. The ECMA standard says that it is always scanned as an expression list, but VC10 doesn't do that. We try to distinguish cases where a CLI array might apply, and only scan an expression list in those cases, thus preserving more C/C++ compatibility. In the case of a class operand, we scan an expression list because the class might be convertible to a handle to CLI array. If it turns out later that the subscripting is a standard C++ case, and more than one expression was scanned, we'll give an error. We do that, rather than converting the expression list to a comma-operator list, because that conversion is dangerous or improper once variadic template pack expansions are allowed in the expression list. */ if (rcblock != NULL) { /* On a rescan, keep the form (expression list or single expression) we already have. */ } else if (cli_array_case || is_overloadable_type_first_operand(operand_1) || is_property_ref_operand(operand_1)) { /* In C++/CLI mode, the [...] brackets contain an expression list instead of a single expression if the array is a C++/CLI array (or might be convertible to one) or a property reference. */ subscript_is_expr_list = TRUE; } /* if */ } /* if */ if (is_property_ref_operand(operand_1) && property_ref_has_accessor_that_yields_subscriptable_object(operand_1)){ /* For a property field reference where there's a "get" accessor that returns something that can be subscripted, use that and then subscript the returned value. */ rewrite_property_reference(operand_1, (an_operand *)NULL, (a_rewritten_property_reference_kind)rprk_none, (a_routine_ptr *)NULL); rewrite_class_with_default_indexed_property_as_property_ref(operand_1); } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (rcblock == NULL && !opnd2_done) { /* Scan the subscript operand if it wasn't already done by the call to scan_function_call above. */ /* Get past the opening bracket. */ (void)get_token(); add_matching_stop_token(tok_rbracket); /* Scan the second operand. */ if (multi_subscript_enabled || subscript_is_expr_list) { /* The subscript is an expression list in C++23 and C++/CLI modes. Top-level commas separate elements of the list. */ operand_2_list = scan_expr_list(tok_rbracket, /*is_delegate_init=*/FALSE, /*is_custom_ms_attr_arg_list=*/FALSE, /*empty_list_okay=*/FALSE, /*trailing_comma_okay=*/FALSE, /*bundle=*/FALSE); if (!subscript_is_expr_list) { /* The cases that permit multiple subscripts were already handled by the call to scan_function_call above. So here we should find only a single operand. */ reduce_singleton_subscript_list(&operand_2_list, &operand_2, /*cli_array=*/FALSE, &pos_curr_token); } /* if */ } else if (curr_token == tok_lbrace && list_init_enabled) { /* In C++11, the expression inside the brackets is allowed to be a brace-enclosed list. */ scan_braced_init_list_as_operand(&operand_2); } else { /* The subscript is the usual single expression. Top-level commas are operators, e.g., x[1, 2] has a single subscript expression that is the comma expression "1, 2". */ scan_expr(&operand_2, PREC_LOWEST, EOPT_SUBSCRIPT_OP); } /* if */ closing_bracket_position = pos_curr_token; } /* if */ if (err) { /* Subscripting is not allowed in this kind of expression. */ make_error_operand(result); operand_will_not_be_used_because_of_error(operand_1); if (!subscript_is_expr_list) { operand_will_not_be_used_because_of_error(&operand_2); } else { arg_list_will_not_be_used_because_of_error(operand_2_list); /* The list is freed at the end below. */ } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED } else if (ms_extensions && is_property_ref_operand(operand_1)) { /* The operand is a field selection for a field declared with the Microsoft property extension (either via __declspec(property(...) or via the C++/CLI syntax). Add the subscript expression to the operand. It will be included as an argument in the call of a "get" or "put" function when this operand is rewritten later. If the subscripts were scanned as a list, add all of them to the existing list. */ an_arg_list_elem_ptr last_subscript; /* Find the end of the existing subscript list. */ last_subscript = operand_1->variant.property_ref.subscripts; if (last_subscript != NULL) { while (!is_last_elem(last_subscript)) { last_subscript = next_elem(last_subscript); } /* while */ } /* if */ /* There should be at least one subscript expression. */ check_assertion(!subscript_is_expr_list || operand_2_list != NULL); if (subscript_is_expr_list) { mark_init_component_list_as_permanently_allocated(operand_2_list); if (last_subscript == NULL) { operand_1->variant.property_ref.subscripts = operand_2_list; } else { append_elem(last_subscript, operand_2_list); } /* if */ operand_2_list = NULL; } else { an_arg_list_elem_ptr subsc = alloc_arg_list_elem_for_operand(&operand_2); mark_init_component_list_as_permanently_allocated(subsc); if (last_subscript == NULL) { operand_1->variant.property_ref.subscripts = subsc; } else { append_elem(last_subscript, subsc); } /* if */ } /* if */ copy_operand(operand_1, result); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else { if (!C_mode() && is_template_dependent_context() && (operand_is_dependent(operand_1) || (subscript_is_expr_list ? arg_list_is_type_dependent(operand_2_list) : operand_is_dependent(&operand_2)))) { /* There is at least one template-dependent operand, so build an expression with a generic operator. We can't go to check_for_operator_overloading when we have an expression list, and it's useful to handle the non-list dependent case separately here also because it simplifies some tests below for __builtin_offsetof etc. */ if (!subscript_is_expr_list) { make_generic_operation_operand(onk_subscript, /*unary_operator=*/FALSE, operand_1, &operand_2, result, &operator_position, operator_tok_seq_number, &closing_bracket_position); } else { /* We have an expression list for operand 2. */ template_multi_subscript_operation(cli_or_cx_enabled ? eok_cli_subscript : eok_subscript, operand_1, operand_2_list, result, &operator_position, operator_tok_seq_number, &closing_bracket_position); } /* if */ processed = TRUE; } else if (!C_mode() && (is_overloadable_type_first_operand(operand_1) || (subscript_is_expr_list ? is_overloadable_type_arg_list(operand_2_list) : is_overloadable_type_operand(&operand_2)))) { /* Look for C++ operator overloading cases. */ a_boolean has_predef_meaning = FALSE; if (subscript_is_expr_list) { /* If we have an expression list, pass the last expression as the second operand. If the list has only one member, that's a full representation of the list. If it has more than one, that models the type and value category the operand would have if it were treated a comma-operator expression. (Even though, for reasons noted above, we don't do that conversion, doing that produces more sensible behavior.) In either case, we want to go to check_for_operator_overloading so we can check for conversions to a handle to CLI array. */ an_arg_list_elem_ptr alep = operand_2_list; check_assertion(alep != NULL); while (!is_last_elem(alep)) alep = next_elem(alep); check_arg_list_elem_is_expression(alep); copy_operand(operand_of_arg_list_elem(alep), &operand_2); } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && (is_class_struct_union_type(operand_1->type) || is_handle_type(operand_1->type))) { /* In C++/CLI, a class operand can be converted to a handle to CLI array, which has a predefined meaning. */ has_predef_meaning = TRUE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ check_for_operator_overloading((an_opname_kind)onk_subscript, /*unary_operator=*/FALSE, /*must_be_member_function=*/TRUE, /*try_conversions=*/TRUE, has_predef_meaning, operand_1, &operand_2, &operator_position, operator_tok_seq_number, (a_nondependent_call_depth)0, &closing_bracket_position, result, &processed); if (processed && (offsetof_case || cli_or_cx_enabled)) { /* An overloaded operator cannot be used with __builtin_offsetof and some C++/CLI cases. Note that template-dependent cases were handled above and don't get here. */ if (is_error_operand(result)) { /* An error is okay. */ } else if (offsetof_case) { expr_pos_error(ec_no_overloaded_subscript_with_offsetof, &operator_position); conv_to_error_operand(result); } else if (subscript_is_expr_list && !is_last_elem(operand_2_list)) { /* If an operand[] was selected, and an expression list containing more than one expression was scanned, give an error. See comment above about not wanting to convert the expression list to a comma-operator expression. */ check_assertion(subscript_is_expr_list); expr_pos_error(ec_comma_operator_in_cli_subscript, init_component_pos(operand_2_list)); conv_to_error_operand(result); } /* if */ } /* if */ } /* if */ #if GNU_VECTOR_TYPES_ALLOWED if (!processed && gnu_mode && gnu_version >= 40600 && is_vector_type(operand_1->type)) { /* GNU vector types can be subscripted using the "x[n]" notation. */ a_type_ptr el_type; an_expr_node_ptr subsc_node, op1_node; do_operand_transformations(operand_1, TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION); do_operand_transformations(&operand_2, TOPT_NO_OPTIONS); if (!is_integral_or_enum_type(operand_2.type)) { error_in_operand(expr_not_integral_or_any_enum_code(), &operand_2); } /* if */ op1_node = make_node_from_operand(operand_1); op1_node->next = make_node_from_operand(&operand_2); el_type = skip_typerefs(op1_node->type)->variant.vector.element_type; subsc_node = make_operator_node(eok_vector_subscript, el_type, op1_node); if (op1_node->is_lvalue) subsc_node->is_lvalue = TRUE; make_lvalue_or_rvalue_expression_operand(subsc_node, result); if (op1_node->is_lvalue) { result->ref_entries_list = operand_1->ref_entries_list; } /* if */ processed = TRUE; } /* if */ #endif /* GNU_VECTOR_TYPES_ALLOWED */ if (!processed) { /* Non-operator-function cases. */ do_operand_transformations(operand_1, TOPT_NO_OPTIONS); #if MICROSOFT_EXTENSIONS_ALLOWED /* If conversions were done, this might now be a CLI array case. */ cli_array_case = is_handle_to_cli_array_type(operand_1->type); if (cli_array_case) { /* A C++/CLI array subscripting case. */ an_arg_list_elem_ptr alep; an_expr_node_ptr subsc_expr_list = NULL, end_subsc_expr_list = NULL; a_host_large_unsigned subsc_count = 0; a_boolean any_subsc_error = FALSE; /* Check the types of the subscripts and link them into a list of expressions. */ check_assertion(!subscript_is_expr_list || operand_2_list != NULL); alep = operand_2_list; for (;;) { an_expr_node_ptr subsc_expr; an_operand *subsc_op; if (subscript_is_expr_list) { check_arg_list_elem_is_expression(alep); subsc_op = operand_of_arg_list_elem(alep); } else { subsc_op = &operand_2; } /* if */ do_operand_transformations(subsc_op, TOPT_NO_OPTIONS); if (!is_template_param_type(subsc_op->type) && !check_integral_or_enum_operand(subsc_op)) { any_subsc_error = TRUE; } /* if */ subsc_expr = make_node_from_operand(subsc_op); if (subsc_expr_list == NULL) { subsc_expr_list = subsc_expr; } else { check_assertion(end_subsc_expr_list != NULL); end_subsc_expr_list->next = subsc_expr; } /* if */ end_subsc_expr_list = subsc_expr; subsc_count++; if (!subscript_is_expr_list) break; alep = next_elem(alep); if (alep == NULL) break; } /* for */ /* The first operand must be a handle to a C++/CLI array. */ if (is_handle_type(operand_1->type)) { a_type_ptr arr_type = type_pointed_to(operand_1->type); if (is_cli_array_type(arr_type)) { /* The first operand is a handle to a CLI array. */ a_boolean unknown; /* Check that the number of subscripts provided matches the array rank. */ if (subsc_count != cli_array_rank(arr_type, &unknown) && !unknown) { if (!any_subsc_error) { expr_pos_error(ec_cli_array_invalid_number_of_subscripts, &operator_position); } /* if */ err = TRUE; } else { result_type = cli_array_element_type(arr_type); } /* if */ } else if (is_template_param_type(arr_type)) { /* Handle to template parameter type is okay -- that might turn out to be a handle to CLI array. */ result_type = type_of_unknown_templ_param_nontype; } else { check_assertion(is_error_type(arr_type)); err = TRUE; } /* if */ } else if (is_template_param_type(operand_1->type)) { /* A first operand of template parameter type is okay -- that might turn out to be a handle. */ result_type = type_of_unknown_templ_param_nontype; } else { check_assertion(is_error_type(operand_1->type)); err = TRUE; } /* if */ if (err) { make_error_operand(result); operand_will_not_be_used_because_of_error(operand_1); } else { /* Make an eok_cli_subscript operation. No folding to a constant is ever possible. */ an_expr_node_ptr subsc_node; an_expr_node_ptr op1_node = make_node_from_operand(operand_1); op1_node->next = subsc_expr_list; subsc_node = make_lvalue_operator_node( (an_expr_operator_kind)eok_cli_subscript, result_type, op1_node); make_glvalue_expression_operand(subsc_node, result); } /* if */ processed = TRUE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ if (!processed) { /* Normal case; not a C++/CLI array or GNU vector. */ an_operand *pointer_operand, *integer_operand; a_type_ptr ptr_op_type; a_boolean pointer_operand_is_second = FALSE; an_error_code err_code = ec_expr_not_pointer_to_object; #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled) err_code = ec_expr_not_pointer_or_array_handle; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (subscript_is_expr_list) { /* If the contents of the [...] were scanned as an expression list, but this did not turn out to be a C++23 multi-subscript or CLI array case, turn an expression list containing a single expression into just an expression, but issue an error for a list containing zero or multiple expressions. */ reduce_singleton_subscript_list(&operand_2_list, &operand_2, cli_array_case, &closing_bracket_position); subscript_is_expr_list = FALSE; } /* if */ do_operand_transformations(&operand_2, TOPT_NO_OPTIONS); /* One of the operands must have type "pointer to object type" and the other must be an integral expression. */ pointer_operand = operand_1; integer_operand = &operand_2; if (is_integral_or_enum_type(operand_1->type)) { /* The subscript value is outside the brackets and the pointer value is inside the brackets. Swap them for the type checking. */ pointer_operand_is_second = TRUE; pointer_operand = &operand_2; integer_operand = operand_1; } /* if */ ptr_op_type = pointer_operand->type; if (gcc_mode && is_pointer_type(ptr_op_type) && is_void_type(type_pointed_to(ptr_op_type))) { /* GNU C allows a pointer to "void" to be subscripted. */ if (!expr_stack->marked_as_gnu_extension) { expr_pos_warning(ec_nonobject_pointer_arithmetic, &operator_position); } /* if */ result_type = type_pointed_to(ptr_op_type); } else if (gpp_version_is(any_version) && is_template_dependent_context() && !scope_stack_top().is_rescan && is_pointer_type(ptr_op_type) && (is_incomplete_type(type_pointed_to(ptr_op_type)) || !is_pointer_to_object_type(ptr_op_type))) { /* GCC allows subscripting pointers-to-incomplete types in template contexts. Handle such cases like the template-dependent cases. */ pos_warning(ec_expr_not_object_pointer, &pointer_operand->position); make_generic_operation_operand((an_opname_kind)onk_subscript, /*unary_operator=*/FALSE, operand_1, &operand_2, result, &operator_position, operator_tok_seq_number, &closing_bracket_position); goto cleanup_and_done; } else if ( /* One operand must be a pointer to object. */ #if PTR_TO_INCOMP_ARRAY_ARITHMETIC_ALLOWED /* Pointer to incomplete array is also allowed. */ check_object_or_incomp_array_pointer_operand( pointer_operand, err_code, integer_operand) #else /* !PTR_TO_INCOMP_ARRAY_ARITHMETIC_ALLOWED */ check_object_pointer_operand(pointer_operand, err_code) #endif /* PTR_TO_INCOMP_ARRAY_ARITHMETIC_ALLOWED */ ) { /* The call to check_object_pointer_operand can change the operand's type (in pre-C99 C modes). */ ptr_op_type = pointer_operand->type; result_type = type_pointed_to(ptr_op_type); } else { result_type = error_type(); } /* if */ /* The other operand must be integral or enum. */ (void)check_integral_or_enum_operand(integer_operand); /* Build the expression. */ /* Note that the integral promotions are NOT done on the subscript; this is as the standard wants it. */ do_binary_operation_full((an_expr_operator_kind)eok_subscript, operand_1, &operand_2, result_type, /*result_is_lvalue=*/TRUE, result, &operator_position, operator_tok_seq_number, &closing_bracket_position); if (!is_error_operand(result)) { if (pointer_operand_is_second) { set_pointer_operand_is_second_flag(result); } /* if */ /* Preserve the reference entries from the pointer operand (the array) because the result is an lvalue. */ result->ref_entries_list = pointer_operand->ref_entries_list; } /* if */ } /* if */ } /* if */ cleanup_and_done: free_arg_list(operand_2_list); if (rcblock == NULL) { #if EXTRA_SOURCE_POSITIONS_IN_IL /* Save the position of the "]". */ end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ (void)required_token(tok_rbracket, ec_exp_rbracket); remove_matching_stop_token(tok_rbracket); } /* if */ set_operand_position(result, &operand_1->position, &end_position, &operator_position); rule_out_expr_kinds(ROEK_INTEGRAL_CONSTANT, result); done: db_exit(); } /* scan_subscript_operator */ static void mark_operand_as_pack_expansion(an_operand *operand, a_pack_expansion_descr_ptr pedep) /* operand is a variadic template pack expansion scanned in a prototype instantiation. Mark it as such. pedep points to the pack expansion description block. */ { check_assertion(pedep != NULL); operand->pack_expansion_descr = pedep; /* We can't set the is_pack_expansion flag on the expression yet because there might be implicit conversions added on top of it later. */ } /* mark_operand_as_pack_expansion */ static void mark_arg_list_elem_as_pack_expansion( an_arg_list_elem_ptr alep, a_pack_expansion_descr_ptr pedep) /* alep is an argument list element that is a variadic template pack expansion scanned in a prototype instantiation. Mark it as such. pedep points to the pack expansion description block. */ { check_assertion(pedep != NULL); alep->pack_expansion_descr = pedep; if (is_expression_component(alep)) { mark_operand_as_pack_expansion(operand_of_arg_list_elem(alep), pedep); } /* if */ } /* mark_arg_list_elem_as_pack_expansion */ static an_init_component_ptr scan_expr_into_new_init_component( a_local_expr_options_set options) /* Allocate an ick_expression init-component, scan an expression into it (with precedence PREC_LOWEST and options as given by "options"), and return a pointer to the init_component. */ { an_init_component_ptr icp = alloc_init_component((an_init_component_kind)ick_expression); a_boolean saved_constant_expr_ruled_out; an_operand *opnd; saved_constant_expr_ruled_out = expr_stack->constant_expr_ruled_out; expr_stack->constant_expr_ruled_out = FALSE; opnd = operand_of_arg_list_elem(icp); scan_expr(opnd, PREC_LOWEST, options); if (expr_stack->constant_expr_ruled_out) { icp->constant_expr_ruled_out = TRUE; } /* if */ if (saved_constant_expr_ruled_out) { expr_stack->constant_expr_ruled_out = TRUE; } /* if */ return icp; } /* scan_expr_into_new_init_component */ static an_init_component_ptr scan_integer_pack_init_components(void) /* Scan an "__integer_pack ( ) ..." construct that appears as an element of an initializer-expression list (e.g., a braced-init-list) and return the resulting list of initializer components. The current token on entry must be tok_integer_pack; the trailing "..." is consumed. When the bound is a concrete nonnegative integer N, the result is a (possibly empty) list of N ick_expression components holding the integer constants 0, 1, ..., N-1. When the bound is dependent, the result is a single dependent ick_expression component that stands for the unexpanded __integer_pack; it is expanded when the enclosing template is instantiated and its initializer is rescanned with a concrete bound. */ { a_template_arg_ptr args = scan_integer_pack(/*record_operands=*/FALSE); an_init_component_ptr head = NULL; an_init_component_ptr *p_icp = &head; a_template_arg_ptr arg; for (arg = args; arg != NULL; arg = arg->next) { an_init_component_ptr icp = alloc_init_component((an_init_component_kind)ick_expression); make_constant_operand(arg->variant.constant, operand_of_arg_list_elem(icp)); *p_icp = icp; p_icp = &icp->next; } /* for */ return head; } /* scan_integer_pack_init_components */ static an_init_component_ptr scan_expr_as_init_component( a_boolean bundle, a_local_expr_options_set options) /* Scan an expression, from source and not from a cache, and return an init component entry describing what was scanned. 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. The expression is scanned with the given options and PREC_LOWEST precedence. */ { an_init_component_ptr icp; an_object_lifetime_ptr wrap_lifetime = NULL; an_object_lifetime_ptr saved_stack_lifetime = NULL; an_object_lifetime_ptr saved_curr_lifetime = NULL; /* scan_expr will consult the current cache, so make sure we're going to get something new (as we expect) and not something cached. */ check_assertion(!cached_initializer_present()); /* If the initializer is to be bundled, we put an object lifetime around each scanned expression. Later, when we know how the expression is used, we may merge the lifetime into a parent expression lifetime. */ if (bundle && curr_object_lifetime != NULL && !curr_expr_kind_is_const()) { saved_curr_lifetime = curr_object_lifetime; if (curr_object_lifetime->kind == olk_expr_temporary) { curr_object_lifetime = curr_object_lifetime->parent_lifetime; } /* if */ push_object_lifetime((an_il_entry_kind)iek_expr_node, (char *)NULL, (an_object_lifetime_kind)olk_expr_temporary); wrap_lifetime = curr_object_lifetime; saved_stack_lifetime = expr_stack->lifetime; expr_stack->lifetime = wrap_lifetime; #if DEBUG if (db_flag_is_set("dump_lifetimes")) { db_object_lifetime_with_indentation(curr_object_lifetime, "About to bundle: "); } /* if */ #endif /* DEBUG */ } /* if */ /* Scan the initializer expression and put it into an init-component. */ icp = scan_expr_into_new_init_component(options); if (wrap_lifetime != NULL) { /* Save the lifetime created for this expression for use later when convert_initializer is called to convert it. Don't save the lifetime if it wasn't really used. */ check_assertion(curr_object_lifetime == wrap_lifetime); if (pop_object_lifetime_full(/*unbound_okay=*/TRUE)) { icp->variant.expr.lifetime = wrap_lifetime; detach_from_object_lifetime_tree(wrap_lifetime); } /* if */ curr_object_lifetime = saved_curr_lifetime; expr_stack->lifetime = saved_stack_lifetime; #if DEBUG if (db_flag_is_set("dump_lifetimes")) { db_object_lifetime_with_indentation(curr_object_lifetime, "Bundling done: "); } /* if */ #endif /* DEBUG */ } /* if */ icp->bundled = bundle; /* Save any reference entries separately from the current expression. */ detach_ref_entries_from_curr_expr(operand_of_arg_list_elem(icp)); icp->detached_ref_entries = TRUE; if (expr_stack->consteval_function_designator_seen) { icp->consteval_function_designator_seen = TRUE; } /* if */ return icp; } /* scan_expr_as_init_component */ static void fold_noncast_expr_if_possible(an_arg_list_elem_ptr alep) /* If alep is an expression component that is not an explicit cast at the top level, attempt to fold it. (This is done to emulate null-pointer-constant behavior of MSVC.) */ { if (is_expression_component(alep)) { an_operand *opnd = operand_of_arg_list_elem(alep); if (is_expression_operand(opnd)) { an_expr_node_ptr node = opnd->variant.expression; if (!(node->compiler_generated && is_cast_operation_node(node))) { force_operand_to_constant_if_possible(opnd); if (microsoft_mode && ms_permissive && op_is_zero_constant(opnd)) { opnd->variant.constant.null_pointer_constant_ruled_out = FALSE; } /* if */ } /* if */ } /* if */ } /* if */ } /* fold_noncast_expr_if_possible */ static a_boolean extract_unsigned_value_from_operand( an_operand *operand, a_host_large_unsigned *value) /* Extract an unsigned value from the given operand, folding an expression operand when needed. Return TRUE if the value was successfully extracted and stored in *value, FALSE otherwise. */ { a_constant_ptr constant = NULL, folded_constant = NULL; a_boolean result = FALSE; if (is_constant_operand(operand) && !is_error_constant(&operand->variant.constant)) { constant = &operand->variant.constant; } else if (is_expression_operand(operand)) { folded_constant = local_constant(); if (fold_constexpr_expr(operand->variant.expression, folded_constant, /*is_constant_evaluated=*/TRUE, /*force_prvalue=*/TRUE)) { constant = folded_constant; } /* if */ } /* if */ if (constant != NULL && !is_error_constant(constant)) { a_boolean ovflo; *value = unsigned_value_of_integer_constant(constant, &ovflo); result = !ovflo; } /* if */ if (folded_constant != NULL) release_local_constant(&folded_constant); return result; } /* extract_unsigned_value_from_operand */ static a_pack_expansion_descr_ptr end_comma_separated_pack_element_scan( a_boolean pack_index_expr, a_pack_expansion_stack_entry_ptr pesep, a_boolean *p_any_more) /* Complete the outer pack expansion context for a list element. If pack_index_expr is TRUE (C++26 pack-index-expression), the context is only completed when a trailing "..." follows; otherwise it is abandoned to prevent inner pack references from being diagnosed as unexpanded. If pack_index_expr is FALSE, the context is always completed and the expansion advances. pesep is the pack expansion stack entry for the outer context. *p_any_more is set to TRUE if more elements remain in the expansion. */ { a_pack_expansion_descr_ptr pedep = NULL; if (pack_index_expr) { if (curr_token == tok_ellipsis) { pedep = end_potential_pack_expansion_context(pesep, /*is_declarator=*/FALSE); if (in_generic_lambda_in_prototype_instantiation()) { /* In a generic-lambda prototype instantiation nested inside a real instantiation, keep the outer "..." dependent for later rescan (when inner template arguments are known) rather than advancing a hybrid pack expansion here. */ *p_any_more = FALSE; abandon_potential_pack_expansion_context(pesep); } else { *p_any_more = advance_to_next_pack_element(pesep); } /* if */ } else { *p_any_more = FALSE; if (pesep != NULL) { abandon_potential_pack_expansion_context(pesep); } /* if */ } /* if */ } else { pedep = end_potential_pack_expansion_context(pesep, /*is_declarator=*/FALSE); *p_any_more = advance_to_next_pack_element(pesep); } /* if */ return pedep; } /* end_comma_separated_pack_element_scan */ static an_arg_list_elem_ptr scan_expr_list( a_token_kind closing_token, a_boolean is_delegate_init, ARG_UNUSED a_boolean is_custom_ms_attr_arg_list, a_boolean empty_list_okay, a_boolean trailing_comma_okay, a_boolean bundle) /* Scan a comma-separated list of expressions. The list must be terminated by the token indicated by closing_token (which is not consumed by this routine). If is_delegate_init is TRUE, this is the initializer list for a C++/CLI gcnew of a delegate type. If is_custom_ms_attr_arg_list is TRUE, this is an argument list for a Microsoft-style bracketed attribute. If empty_list_okay is TRUE, an empty list is allowed. If trailing_comma_okay is TRUE, the last expression may be followed by a comma (which is consumed here). bundle is TRUE if the expressions should be "bundled," meaning packaged with related information so they can be saved off to the side (e.g., in an initializer cache) for later restoration and further processing. A pointer to the resulting argument list is returned. */ { a_boolean after_cached_expr = FALSE; an_arg_list_elem_ptr alep; an_arg_list_elem_ptr expr_list = NULL, end_expr_list = NULL; a_local_expr_options_set options = EOPT_DISALLOW_COMMA_OPERATOR; if (is_delegate_init) options |= EOPT_DELEGATE_INITIALIZER; /* Pick up any cached expressions first. */ while (cached_initializer_present()) { expr_list = expr_stack->initializer_cache->first_init; end_expr_list = expr_stack->initializer_cache->last_init; clear_initializer_cache(expr_stack->initializer_cache); if (!bundle) { unbundle_init_component_list_expressions(expr_list); } /* if */ if (microsoft_mode && ms_permissive) { for (alep = expr_list; alep != NULL; alep = next_elem(alep)) { /* In some cases, we need to treat constant zero values as null pointer constants. Just in case, attempt to fold the expression. */ fold_noncast_expr_if_possible(alep); } /* for */ } /* if */ after_cached_expr = TRUE; } /* while */ /* Check for an empty argument list (or the end, if we picked up some cached expressions). */ if (curr_token != closing_token || (!empty_list_okay && !after_cached_expr)) { add_matching_stop_token(closing_token); add_stop_token(tok_comma); /* Scan a comma-separated list of arguments. */ do { a_pack_expansion_stack_entry_ptr pesep; a_boolean any_more; a_boolean pack_index_expr; if (after_cached_expr) { /* If we took one or more cached expressions above, we're already after the expression, so go to where we process the comma for the next iteration. */ after_cached_expr = FALSE; continue; } /* if */ if (trailing_comma_okay) { /* Allow an extra comma at the end of the argument list. */ if (curr_token == closing_token) break; } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED /* If we are scanning the argument list of a custom Microsoft attribute, stop scanning when a named argument is encountered. These arguments will be scanned by scan_custom_ms_attribute_named_arg_list. */ if (is_custom_ms_attr_arg_list && curr_token == tok_identifier && next_token() == tok_assign) { break; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Each expression on the list is potentially a pack expansion ended by "...". */ /* Note that the code here is very similar to scan_potential_pack_expansion_initializer_expr and scan_expression_list_context_expr. */ pack_index_expr = pack_index_next(); any_more = begin_potential_pack_expansion_context(&pesep); while (any_more) { /* Scan another element for the list. */ a_pack_expansion_descr_ptr pedep; if (curr_token == tok_integer_pack) { /* An "__integer_pack(N)..." construct used as a list element. It expands in place for a concrete N, or yields a single dependent placeholder that is expanded at instantiation. The construct consumes its own trailing "...", so the potential pack expansion context started above is simply closed. */ an_init_component_ptr pack_list; pack_list = scan_integer_pack_init_components(); if (pack_list != NULL) { an_init_component_ptr pack_tail = pack_list; if (expr_list == NULL) { expr_list = pack_list; } else { append_elem(end_expr_list, pack_list); } /* if */ for (; pack_tail->next != NULL; pack_tail = pack_tail->next) ; end_expr_list = pack_tail; } /* if */ (void)end_potential_pack_expansion_context(pesep, /*is_declarator=*/FALSE); any_more = advance_to_next_pack_element(pesep); continue; } /* if */ if (curr_token == tok_lbrace && list_init_enabled) { /* A brace-enclosed list. */ alep = parse_braced_init_list(bundle); check_nonstd_list_init(init_component_pos(alep)); } else { /* An expression. */ if (bundle) { /* In addition to bundling the lifetime representation this call also detaches the associated ref entries from the current expression. */ alep = scan_expr_as_init_component(bundle, options); } else { alep = scan_expr_into_new_init_component(options); } /* if */ /* In some cases, we need to treat constant zero values as null pointer constants. Just in case, attempt to fold the expression. */ if (microsoft_mode && ms_permissive) { fold_noncast_expr_if_possible(alep); } /* if */ } /* if */ /* Add the expression or braced-init-list to the list. */ if (expr_list == NULL) { expr_list = alep; } else { append_elem(end_expr_list, alep); } /* if */ end_expr_list = alep; /* If this is a pack expansion, swallow the trailing "..." and loop for the next iteration of the expansion. */ pedep = end_comma_separated_pack_element_scan(pack_index_expr, pesep, &any_more); if (pedep != NULL) { /* This element is a variadic template pack expansion, i.e., it's followed by "...". Furthermore, we're in the prototype instantiation, so we record the expansion information on the element. */ mark_arg_list_elem_as_pack_expansion(alep, pedep); } /* if */ } /* while */ } while (loop_token(tok_comma)); remove_stop_token(tok_comma); remove_matching_stop_token(closing_token); } /* if */ return expr_list; } /* scan_expr_list */ static void scan_expression_list_context_expr( a_local_expr_options_set options, a_boolean allow_braced_init_list, an_operand *operand, an_operand *bound_function_selector, a_boolean *expr_not_present) /* Scan a single expression that happens to be in a context that syntactically is considered an expression list, and return the expression in *operand. Because the context is nominally an expression list, allow pack expansions and disallow top-level comma operators. Allow a braced-init-list if allow_braced_init_list is TRUE (and the current dialect allows them). If an expression is present but an empty pack expansion produces no result expression, return *expr_not_present TRUE (and nothing in *operand). If bound_function_selector is non-NULL, the expression is allowed to be a bound function, and if it is, the selector part is returned in *bound_function_selector. options is a set of expression-scanning options in case the caller wants to provide some additional ones over the basic ones implied for this case. */ { a_pack_expansion_stack_entry_ptr pesep; a_boolean any_more, first_time = TRUE; a_boolean error_issued = FALSE; a_boolean pack_index_expr; *expr_not_present = TRUE; options |= EOPT_DISALLOW_COMMA_OPERATOR; if (!C_mode() && bound_function_selector != NULL) { options |= EOPT_ALLOW_BOUND_FUNCTION; } /* if */ /* Note that the code here is very similar to scan_expr_list and scan_potential_pack_expansion_initializer_expr. */ pack_index_expr = pack_index_next(); do { any_more = begin_potential_pack_expansion_context(&pesep); /* Skip over any empty pack expansions. */ } while (!pack_index_expr && !any_more && loop_token(tok_comma)); while (any_more) { an_operand local_operand, local_bound_function_selector; a_pack_expansion_descr_ptr pedep; if (allow_braced_init_list && list_init_enabled && curr_token == tok_lbrace) { scan_braced_init_list_as_operand(&local_operand); bound_function_selector = NULL; } else { scan_expr_full(&local_operand, &local_bound_function_selector, PREC_LOWEST, options); if (!local_operand.bound_function) { bound_function_selector = NULL; } /* if */ } /* if */ if (first_time) { copy_operand(&local_operand, operand); if (bound_function_selector != NULL) { copy_operand(&local_bound_function_selector, bound_function_selector); } /* if */ *expr_not_present = FALSE; } else { /* More than one expression from a pack expansion. Issue one error. */ if (!error_issued) { expr_pos_error(ec_excess_pack_expansion, &local_operand.position); error_issued = TRUE; } /* if */ operand_will_not_be_used_because_of_error(&local_operand); } /* if */ /* If this is a pack expansion, swallow the trailing "..." and loop for the next iteration of the expansion. */ pedep = end_comma_separated_pack_element_scan(pack_index_expr, pesep, &any_more); if (pedep != NULL) { /* This expression is a variadic template pack expansion, i.e., it's followed by "...". Furthermore, we're in the prototype instantiation, so we record the expansion information on the expression. */ check_assertion(first_time); mark_operand_as_pack_expansion(operand, pedep); } /* if */ first_time = FALSE; } /* while */ if (!pack_index_expr) skip_empty_pack_expansions_after_comma(); } /* scan_expression_list_context_expr */ static void rescan_pack_expansion(an_expr_node_ptr expr, an_arg_list_elem_ptr *expr_list, an_arg_list_elem_ptr *end_expr_list, a_rescan_control_block *rcblock) /* Expand a variadic template pack expansion as part of redoing semantic analysis on a previously-scanned expression list. expr is the pack expansion expression (i.e., one followed by "..."), which is a member of the expression list. *expr_list and *end_expr_list point to the start and end of the already-rescanned preceding expressions on the expression list; any expressions generated by this pack expansion are added to the end of that list. rcblock gives the rescan control information, including information on the template parameter substitutions to be done. */ { a_boolean any_more; an_expr_rescan_info_entry_ptr eriep; a_pack_expansion_descr_ptr pedep; a_pack_expansion_stack_entry_ptr pesep; a_boolean err; a_boolean add_expr_copy = FALSE; /* Note this is similar to rescan_expression_list_context_expr. */ eriep = get_expr_rescan_info(expr, (an_expr_rescan_info_entry *)NULL); pedep = eriep->saved_operand.pack_expansion_descr; check_assertion(expr->is_pack_expansion && pedep != NULL); if ((rcblock->options & CTWS_ADJUST_COORDINATES) != 0 || (rcblock->ctws_state->in_parent_substitution && !pedep->uses_any_enclosing_packs) || ((rcblock->options & CTWS_ALIAS_DEDUCTION_GUIDE) != 0 && pack_expansion_maps_to_unexpanded_pack(pedep, rcblock->template_param_list, rcblock->template_arg_list))) { add_expr_copy = TRUE; } else { Value_saver unexpanded_pack_saver( &rcblock->ctws_state->unexpanded_pack); rcblock->ctws_state->unexpanded_pack = FALSE; 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); if (!any_more && rcblock->ctws_state->unexpanded_pack && (rcblock->options & CTWS_MAY_BE_RESCANNED) != 0) { add_expr_copy = TRUE; } /* if */ unexpanded_pack_saver.restore_now(); while (any_more) { /* Rescan one iteration of the pack expansion and add the resulting expression to the list. */ an_arg_list_elem_ptr alep = rescan_expr_as_arg_list_elem(expr, rcblock); if (*expr_list == NULL) { *expr_list = alep; } else { check_assertion(*end_expr_list != NULL); append_elem(*end_expr_list, alep); } /* if */ *end_expr_list = alep; (void)end_potential_pack_expansion_context(pesep, /*is_declarator=*/FALSE); any_more = advance_to_next_pack_element(pesep); } /* while */ } /* if */ if (add_expr_copy || ((rcblock->options & CTWS_PRESERVE_DEDUCED_PACKS) != 0 && !pedep->uses_only_enclosing_packs)) { /* We're in a context where some pack elements might have been specified explicitly, but more such elements might be deduced. We have to ensure that the substituted list keeps the ability to gain more elements (i.e., this is really a dependent expression list still). Append a copy of the parameterized pack expansion. We also add a copy of the original expression (which might not be a deduced pack) if the code above suppressed the expansion because it was not needed. */ an_expr_node_ptr expr_copy = copy_expr_tree(expr, CE_PRESERVE_RESCAN_INFO); an_operand opnd; an_arg_list_elem_ptr alep; expr_copy->is_pack_expansion = TRUE; make_expression_operand(expr_copy, &opnd); alep = alloc_arg_list_elem_for_operand(&opnd); alep->pack_expansion_descr = pedep; if (*expr_list == NULL) { *expr_list = alep; } else { alep->preserved_deduced_pack = (rcblock->options & CTWS_PRESERVE_DEDUCED_PACKS) != 0; append_elem(*end_expr_list, alep); } /* if */ *end_expr_list = alep; } /* if */ } /* rescan_pack_expansion */ an_arg_list_elem_ptr rescan_expr_list(an_expr_node_ptr src_list, a_rescan_control_block *rcblock) /* Redo semantic analysis on a previously-scanned expression list, substituting for template parameters as described in rcblock, and return a list of arg-list-element entries for the expressions on the list. Expand variadic template pack expansions into multiple expressions as necessary. */ { an_expr_node_ptr arg_expr; an_arg_list_elem_ptr alep; an_arg_list_elem_ptr expr_list = NULL, end_expr_list = NULL; if (cached_initializer_present()) { /* Pick up any cached expressions instead of rescanning them. It's assumed that the cached expressions are replacing all the expressions on the rescan list. That works because rescanned expressions are used during the rescan only for "auto" expressions, and those appear alone on a list. If we couldn't count on that, we'd have to figure out how many expressions on the rescan list correspond to cached expressions so we could throw away just that number. */ do { alep = fetch_init_component_from_initializer_cache( expr_stack->initializer_cache); if (expr_list == NULL) { expr_list = alep; } else { check_assertion(end_expr_list != NULL); append_elem(end_expr_list, alep); } /* if */ end_expr_list = alep; } while (cached_initializer_present()); } else { /* No cached expressions; rescan the expression list. */ for (arg_expr = src_list; arg_expr != NULL && !arg_expr->generated_default_arg; arg_expr = arg_expr->next) { if (arg_expr->is_pack_expansion) { /* A variadic template pack expansion. */ rescan_pack_expansion(arg_expr, &expr_list, &end_expr_list, rcblock); } else { /* Normal case, not a pack expansion. */ alep = rescan_expr_as_arg_list_elem(arg_expr, rcblock); if (expr_list == NULL) { expr_list = alep; } else { check_assertion(end_expr_list != NULL); append_elem(end_expr_list, alep); } /* if */ end_expr_list = alep; } /* if */ } /* for */ } /* if */ return expr_list; } /* rescan_expr_list */ static void rescan_expression_list_context_expr( an_expr_node_ptr expr, a_rescan_control_block *rcblock, a_local_expr_options_set options, an_operand *operand, an_operand *bound_function_selector, a_boolean *expr_not_present) /* Redo semantic analysis on a single expression that happens to be in a context that syntactically is considered an expression list, substituting for template parameters as described by rcblock, and return the expression in *operand. Expand variadic template pack expansions as necessary. If an expression is present but an empty pack expansion produces no result expression, return *expr_not_present TRUE (and nothing in *operand). If bound_function_selector is non-NULL, the expression is allowed to be a bound function, and if it is, the selector part is returned in *bound_function_selector. options is a set of expression-scanning options in case the caller wants to provide some additional ones over the basic ones implied for this case. */ { a_boolean first_time = TRUE; *expr_not_present = TRUE; /* We need to go through the whole list to check that it only contains a single expression after any pack expansions. */ while (expr != NULL) { if (expr->is_pack_expansion) { /* A variadic template pack expansion. */ a_boolean any_more; an_expr_rescan_info_entry_ptr eriep; a_pack_expansion_descr_ptr pedep; a_pack_expansion_stack_entry_ptr pesep; a_boolean err; /* Note this is similar to rescan_pack_expansion. */ eriep = get_expr_rescan_info(expr, (an_expr_rescan_info_entry *)NULL); pedep = eriep->saved_operand.pack_expansion_descr; check_assertion(pedep != NULL); 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) { an_operand local_operand, local_bound_function_selector; clear_operand(ok_error, &local_operand); clear_operand(ok_error, &local_bound_function_selector); make_rescan_operand_full(expr, rcblock, options, &local_operand, &local_bound_function_selector); if (first_time) { copy_operand(&local_operand, operand); if (bound_function_selector != NULL) { copy_operand(&local_bound_function_selector, bound_function_selector); } /* if */ *expr_not_present = FALSE; } else { /* More than one expression from a pack expansion. */ subst_fail(rcblock->error_detected); /* It seems safest to allow the loop to run to its normal end. */ } /* if */ (void)end_potential_pack_expansion_context(pesep, /*is_declarator=*/FALSE); any_more = advance_to_next_pack_element(pesep); if (any_more) { a_pack_reference_ptr prp = pesep->instantiation_descr->pack_status; if (prp->kind == prk_parameter && prp->curr_argument.param_type->is_parameter_pack) { /* This is the parameter pack that terminates a substituted parameter pack expansion that is created under the option CTWS_PRESERVE_DEDUCED_PACKS. It should not be treated as another expression in the expansion. */ any_more = FALSE; } /* if */ } /* if */ first_time = FALSE; } /* while */ } else if (first_time) { /* Normal case, not a pack expansion. */ make_rescan_operand_full(expr, rcblock, options, operand, bound_function_selector); *expr_not_present = FALSE; first_time = FALSE; } else { /* More than one expression. */ subst_fail(rcblock->error_detected); break; } /* if */ expr = expr->next; } /* while */ } /* rescan_expression_list_context_expr */ static void scan_call_arguments( a_type_ptr function_type, a_routine_ptr routine, a_token_kind closing_delim, a_boolean already_after_left_delim, an_expr_node_ptr *p_argument_list, a_boolean return_raw_arguments, a_boolean *p_unknown_dependent_function, a_boolean args_will_be_discarded, a_boolean is_custom_ms_attr_arg_list, a_rescan_control_block *rcblock, a_boolean arg_list_supplied, an_arg_list_elem_ptr supplied_arg_list, an_arg_list_elem_ptr *p_arg_list, an_operand *single_operand, a_boolean *single_operand_returned, a_source_position *closing_delim_position) /* Scan the arguments of a function call or C++23 subscript construct and return a list of argument expressions in *p_argument_list. The type of the function being called is given by function_type; function_type is NULL if the type is not known a priori (e.g., for an overloaded function case) or if this is called for a C++23 subscript construct (which is treated as an overloaded function case). routine points to the routine being called; it's NULL if the specific function being called is not known, e.g., when calling through a pointer. *p_unknown_dependent_function is TRUE if the function to be called is not known because it is specified by a template-dependent expression. This function might set *p_unknown_dependent_function to TRUE in some modes if it scans a template-dependent argument list in certain contexts; in that case, raw arguments are returned also (i.e., as if return_raw_arguments were TRUE). args_will_be_discarded is TRUE if the arguments will be discarded, e.g., because the function to be called is not known because of an error. The arguments are returned anyway, in case one wants to link them together in an argument list, but they are not checked nor converted to a parameter type, and unusual operand kinds are replaced by error operands. If is_custom_ms_attr_arg_list, this call if for the arguments in a Microsoft- style bracketed attribute. The current token is the "(" or "[" of the argument list if already_after_left_delim is FALSE, or the token following the left delimiter if already_after_left_delim is TRUE. (The add_stop_token call has not been done in either of those cases.) On return, the current token is the token following the closing ")" or "]". If return_raw_arguments is TRUE, just scan and return the arguments as an argument expression list in *p_arg_list; do not check the arguments against any specific parameter list and do not set *p_argument_list (this is used, for example, when scanning the arguments for an overloaded function). If closing_delim_position is non-NULL, *closing_delim_position is set to the source position of the closing delimiter of the call (but it's not set on a rescan). If rcblock is non-NULL, redo semantic analysis on a previously-scanned argument list, given by rcblock->argument_list. The arguments are returned in either *p_argument_list or *p_arg_list, as specified by return_raw_arguments. already_after_left_delim is ignored. *closing_delim_position is not set or altered. If arg_list_supplied is TRUE, a third interface alternative: the possibly-empty list of arguments is supplied by supplied_arg_list, and no source is scanned. supplied_arg_list is not freed by this routine. If the expression stack indicates that one or more expressions have been cached, those are consumed before any more expressions are read from source or created by rescanning. If single_operand is non-NULL, then if the argument list contains exactly one expression, return it in *single_operand instead of any other processing specified, and return *single_operand_returned set to TRUE. */ { an_arg_list_elem_ptr arg_list; a_boolean unknown_dependent_function; a_boolean arg_list_allocated_locally = FALSE; an_arg_check_block arg_block; a_boolean scanning_source =(rcblock == NULL && !arg_list_supplied && !initializing_coroutine_descriptor()); a_boolean saved_allow_call_with_incomplete_return_type; a_boolean saved_in_call_argument; db_enter(4, "scan_call_arguments"); /* Allowing a call with incomplete return type doesn't propagate to calls in the arguments of the current call. */ if (p_unknown_dependent_function == NULL) { unknown_dependent_function = FALSE; } else { unknown_dependent_function = *p_unknown_dependent_function; } /* if */ saved_allow_call_with_incomplete_return_type = expr_stack->allow_call_with_incomplete_return_type; expr_stack->allow_call_with_incomplete_return_type = FALSE; saved_in_call_argument = expr_stack->in_call_argument; expr_stack->in_call_argument = TRUE; if (p_arg_list != NULL) *p_arg_list = NULL; if (single_operand_returned != NULL) *single_operand_returned = FALSE; if (return_raw_arguments) { /* When asked to scan raw arguments, we don't know anything about the function being called. */ function_type = NULL; routine = NULL; } /* if */ /* Set the block used for checking argument types. */ start_call_argument_processing(function_type, routine, &arg_block); if (unknown_dependent_function) { /* The function to be called is unknown because it's template-dependent. */ check_assertion(!return_raw_arguments && function_type == NULL); arg_block.unknown_dependent_function = TRUE; } else if (args_will_be_discarded) { /* Arguments will be discarded, e.g., because of an error. */ arg_block.args_will_be_discarded = TRUE; } /* if */ if (arg_list_supplied) { /* Use the argument list supplied. rcblock can be non-NULL here if the expression list had to be previously re-scanned already. */ arg_list = supplied_arg_list; arg_block.closing_paren_position = pos_curr_token; if (rcblock == NULL && closing_delim_position != NULL) { *closing_delim_position = pos_curr_token; } /* if */ } else if (rcblock != NULL) { /* Convert the previously-scanned rcblock->argument_list list of expressions into an argument list. */ arg_list = rescan_expr_list(rcblock->argument_list, rcblock); arg_list_allocated_locally = TRUE; /* closing_delim_position is deliberately not set. At this level in the rescan we only know about the arguments, and not about the surrounding delimiters. It's not set to NULL because the caller is likely to have set it correctly already. */ } else { /* The argument list needs to be scanned from source. */ if (!already_after_left_delim) { /* Get past the opening delimiter. */ (void)get_token(); } /* if */ /* Scan the argument list. */ arg_list = scan_expr_list(closing_delim, /*is_delegate_init=*/FALSE, is_custom_ms_attr_arg_list, /*empty_list_okay=*/TRUE, /*trailing_comma_okay=*/any_cfront_mode(), /*bundle=*/FALSE); arg_list_allocated_locally = TRUE; set_err_pos_to_curr_token(); arg_block.closing_paren_position = pos_curr_token; if (closing_delim_position != NULL) { *closing_delim_position = pos_curr_token; } /* if */ if ((gpp_version_is(any_version) || clangcpp_version_is(any_version)) && routine != NULL && routine->source_corresp.is_class_member && scope_stack_top().in_prototype_instantiation && p_unknown_dependent_function != NULL && !unknown_dependent_function && arg_list_is_instantiation_dependent(arg_list)) { arg_block.unknown_dependent_function = TRUE; *p_unknown_dependent_function = TRUE; return_raw_arguments = TRUE; } /* if */ } /* if */ if (single_operand != NULL && is_single_elem(arg_list) && is_expression_component(arg_list)) { /* Return a single operand through *single_operand. */ copy_operand(operand_of_arg_list_elem(arg_list), single_operand); mark_expr_of_operand_as_pack_expansion_if_necessary(single_operand); if (arg_list_allocated_locally) free_arg_list(arg_list); check_assertion(single_operand_returned != NULL); *single_operand_returned = TRUE; } else if (return_raw_arguments) { check_assertion(p_arg_list != NULL); *p_arg_list = arg_list; *p_argument_list = NULL; } else { /* Check and transform the call arguments based on the parameter list. */ process_call_argument_list(arg_list, &arg_block); /* Return argument list pointer to caller. */ *p_argument_list = arg_block.argument_head; if (arg_list_allocated_locally) free_arg_list(arg_list); } /* if */ if (scanning_source) { #if EXTRA_SOURCE_POSITIONS_IN_IL curr_construct_end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ if (!is_custom_ms_attr_arg_list && closing_delim == tok_rparen) { /* Check for the closing delimiter. */ (void)required_token(closing_delim, ec_exp_rparen); } /* if */ } /* if */ /* Restore the previous state wrt. allowing an incomplete return type for the current call. */ expr_stack->in_call_argument = saved_in_call_argument; expr_stack->allow_call_with_incomplete_return_type = saved_allow_call_with_incomplete_return_type; db_exit(); } /* scan_call_arguments */ void scan_dependent_parenthesized_initializer( a_rescan_control_block *rcblock, a_boolean arg_list_supplied, an_arg_list_elem_ptr supplied_arg_list, a_boolean is_custom_ms_attr_arg_list, an_operand *single_operand, a_dynamic_init_ptr *dip) /* Scan and process a parenthesized list of expressions that is the initializer of an entity of a template-dependent type. Build a dynamic initialization entry for the initialization and return a pointer to it in *dip. On entry, the current token is the one following the opening parenthesis. On return, the current token is the one following the closing parenthesis. If rcblock is non-NULL, redo semantic analysis on a previously-scanned initializer list (given by rcblock->argument_list) and return the result as usual (or an error indication in *rcblock). If arg_list_supplied is TRUE, a third interface alternative: the possibly-empty list of arguments is supplied by supplied_arg_list, and no source is scanned. supplied_arg_list is not freed by this routine. If single_operand is non-NULL, then if the argument list contains exactly one expression, return it in *single_operand instead of any other processing specified, and return *dip set to NULL. */ { an_expr_node_ptr expr_arg_list; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position = null_source_position; a_boolean scanning_source = (rcblock == NULL && !arg_list_supplied); #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ a_boolean single_operand_returned; a_boolean unknown_dependent_function = TRUE; /* Scan the argument list. */ scan_call_arguments((a_type_ptr)NULL, (a_routine_ptr)NULL, tok_rparen, /*already_after_left_delim=*/TRUE, &expr_arg_list, /*return_raw_arguments=*/FALSE, &unknown_dependent_function, /*args_will_be_discarded=*/FALSE, is_custom_ms_attr_arg_list, rcblock, arg_list_supplied, supplied_arg_list, (an_arg_list_elem **)NULL, single_operand, &single_operand_returned, (a_source_position *)NULL); #if EXTRA_SOURCE_POSITIONS_IN_IL if (scanning_source) end_position = curr_construct_end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ if (single_operand_returned) { *dip = NULL; } else { /* Set the dynamic init entry to represent "constructor" initialization, leaving the constructor pointer NULL. */ *dip = alloc_expr_ctor_dynamic_init((a_routine_ptr)NULL, expr_arg_list, (a_type_ptr)NULL, /*static_temp=*/FALSE, /*add_default_args=*/FALSE, /*implied_source=*/FALSE, /*value_init=*/FALSE, /*sequenced_args=*/FALSE, /*fold_constexpr=*/FALSE, /*check_constexpr=*/FALSE, (a_source_position *)NULL); } /* if */ #if EXTRA_SOURCE_POSITIONS_IN_IL if (scanning_source) curr_construct_end_position = end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* scan_dependent_parenthesized_initializer */ static void scan_error_parenthesized_initializer( a_rescan_control_block *rcblock, a_boolean arg_list_supplied, an_arg_list_elem_ptr supplied_arg_list) /* Scan and discard a list of expressions that are inside a parenthesized initializer in a context where the destination type is unknown because of an error. In such cases, a list of expressions must be scanned because the target type might have been a class with a constructor. On entry, the current token is after the opening parenthesis. On return, it is following the closing parenthesis. If rcblock is non-NULL, we are redoing semantic analysis on a previously-scanned initializer list; do nothing. Likewise, if arg_list_supplied is TRUE, we have an argument list created some other way; discard the operands on the list given by supplied_arg_list (but do not free the list). */ { if (arg_list_supplied) { /* Argument list was already created. */ arg_list_will_not_be_used_because_of_error(supplied_arg_list); } else if (rcblock != NULL && !cached_initializer_present()) { /* Rescanning an initializer. */ } else { /* Scan the argument list. */ an_expr_node_ptr expr_arg_list; an_arg_list_elem_ptr arg_list; scan_call_arguments((a_type_ptr)NULL, (a_routine_ptr)NULL, tok_rparen, /*already_after_left_delim=*/TRUE, &expr_arg_list, /*return_raw_arguments=*/TRUE, /*p_unknown_dependent_function=*/NULL, /*args_will_be_discarded=*/TRUE, /*is_custom_ms_attr_arg_list=*/FALSE, rcblock, /*arg_list_supplied=*/FALSE, (an_arg_list_elem *)NULL, &arg_list, (an_operand *)NULL, (a_boolean *)NULL, (a_source_position *)NULL); arg_list_will_not_be_used_because_of_error(arg_list); free_arg_list(arg_list); } /* if */ } /* scan_error_parenthesized_initializer */ void check_closing_paren_after_expr_list(void) /* Check for the required closing parenthesis after an expression list, and pass over it if it is found. This is similar to required_token, but it does some special error-recovery processing to handle additional unexpected expressions more gracefully. */ { if (initializing_coroutine_descriptor()) { /* We're currently generating expressions related to initializing a coroutine descriptor block - no closing paren is expected. */ } else if (cached_initializer_present()) { /* There's a cached expression, so the closing paren is not "next". */ an_init_component_ptr icp = expr_stack->initializer_cache->first_init; expr_pos_error(ec_exp_rparen, init_component_pos(icp)); flush_initializer_cache(expr_stack->initializer_cache); } else { a_token_set_array_element save_comma_stop_token_count; a_token_set_array_element *comma_entry_ptr; /* Remove comma from the stop tokens set. */ comma_entry_ptr = &(curr_stop_token_stack_entry-> stop_tokens[(int)tok_comma]); save_comma_stop_token_count = *comma_entry_ptr; *comma_entry_ptr = 0; (void)required_token(tok_rparen, ec_exp_rparen); /* Restore comma as a stop token (if it was one). */ *comma_entry_ptr = save_comma_stop_token_count; } /* if */ } /* check_closing_paren_after_expr_list */ void skip_empty_pack_expansions_after_comma() /* Skip empty pack expansions that follow a tok_comma. This may be needed in cases where something like "X, Y..." needs to be accepted when "Y..." results in an empty expansion. Calling this has no effect if the current token is not a comma, or if called outside of a variadic template context. */ { a_pack_expansion_stack_entry_ptr pesep; if (curr_token == tok_comma && is_variadic_template_context()) { a_tiny_scanning_token_cache cache; cache_curr_token(cache.ptr()); do { (void)get_token(); /* Begin the potential pack expansion just to check for an empty expansion. */ if (begin_potential_pack_expansion_context(&pesep)) { abandon_potential_pack_expansion_context(pesep); rescan_cached_tokens(cache.ptr()); break; } /* if */ } while (curr_token == tok_comma); } /* if */ } /* skip_empty_pack_expansions_after_comma */ static an_expr_node_ptr scan_parenthesized_initializer_expression( a_decl_parse_state *dps, a_rescan_control_block *rcblock, a_type_ptr dest_type, an_error_code err_code, a_boolean *expr_not_present) /* Scan a single expression in parentheses as an initializer value, and convert it to dest_type if necessary. The current token is the token after the opening left parenthesis. On return, the current token is the token following the closing parenthesis. If the conversion cannot be done, issue the error err_code. If expr_not_present is non-NULL, and an expression is present but it's a pack expansion that produces zero expressions, return NULL and *expr_not_present set to TRUE (in that case, the closing parenthesis is not consumed here). The entity being initialized is assumed not to be a variable. dps provides information about the declaration context, but it's fabricated and not indicative of a real declaration. If rcblock is non-NULL, redo semantic analysis on a previously-scanned initializer expression given by rcblock->argument_list, and return the result as usual (or an error indication in *rcblock). */ { an_expr_node_ptr expr; an_operand result; an_init_component_ptr icp = NULL; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ a_boolean have_result = TRUE; check_assertion(dps != NULL); if (expr_not_present != NULL) *expr_not_present = FALSE; if (rcblock == NULL) add_matching_stop_token(tok_rparen); /* Note that if there is a cached expression we take it in preference to rescanning from rcblock. */ if (rcblock != NULL && !cached_initializer_present()) { an_expr_node_ptr arg_list = rcblock->argument_list; if (arg_list->is_pack_expansion) { /* For a pack expansion, rescan it as an expression list, but make sure the result is a singleton. */ icp = rescan_expr_list(arg_list, rcblock); if (icp == NULL || (icp->next != NULL && !icp->next->preserved_deduced_pack)) { if (icp == NULL) { if (expr_not_present != NULL) *expr_not_present = TRUE; } else { subst_fail(rcblock->error_detected); } /* if */ free_init_component_list(icp); have_result = FALSE; } /* if */ } else { make_rescan_operand(arg_list, rcblock, &result); } /* if */ } else { dps->initializer_is_expr_list = TRUE; dps->initializer_is_single_expr = TRUE; icp = scan_init_component_with_potential_pack_expansion( dps, /*bundle=*/FALSE, /*parenthesized=*/TRUE, expr_not_present); if (icp == NULL) have_result = FALSE; } /* if */ if (have_result) { /* Convert to the required type. */ if (icp != NULL) { prep_list_initializer(icp, dest_type, /*is_direct_init=*/TRUE, /*check_narrowing=*/FALSE, /*warning_on_narrowing=*/FALSE, CCO_DEFAULT, /*fill_in_dtor=*/TRUE, /*force_temp=*/FALSE, /*make_lvalue_temp=*/FALSE, &result, (an_init_state *)NULL, (an_arg_match_summary *)NULL); free_init_component_list(icp); } else { prep_initializer_operand(&result, dest_type, (a_boolean *)NULL, (a_conv_descr_ptr)NULL, /*is_copy_initialization=*/FALSE, CCO_DEFAULT, err_code); } /* if */ if (rcblock == NULL) { /* Check for the required closing parenthesis. */ #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ if (gpp_mode || microsoft_mode) { /* GNU and Microsoft compilers don't diagnose empty pack expansions at this point. I.e., something like "X, Y..." where "Y..." results in an empty expansion is accepted. */ skip_empty_pack_expansions_after_comma(); } /* if */ check_closing_paren_after_expr_list(); #if EXTRA_SOURCE_POSITIONS_IN_IL curr_construct_end_position = end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } else { /* We are rescanning the initializer expression. If there were additional expressions, the rescan should fail (this parallels a failure in the call to check_closing_paren_after_expr_list in the non-rescan case). */ if (rcblock->argument_list != NULL && rcblock->argument_list->next != NULL) { subst_fail(rcblock->error_detected); } /* if */ } /* if */ expr = make_node_from_operand_for_expr_list(&result); } else { /* No result because the expression is a pack expansion that expanded to zero expressions. */ expr = NULL; } /* if */ if (rcblock == NULL) remove_matching_stop_token(tok_rparen); return expr; } /* scan_parenthesized_initializer_expression */ void scan_ctor_arguments(a_symbol_ptr constructor_sym, a_source_position *source_pos, a_type_ptr object_class_type, a_type_ptr dest_type, a_boolean fill_in_dtor, a_boolean elision_allowed, a_boolean is_custom_ms_attr_arg_list, a_conv_context_set conv_context, a_rescan_control_block *rcblock, a_boolean arg_list_supplied, an_arg_list_elem_ptr supplied_arg_list, an_arg_list_elem_ptr init_list_ctor_arg_list, a_boolean *trivial_ctor, a_boolean *explicit_ctor, a_boolean *elision_done, a_boolean *unboxing_conv, a_boolean *string_ctor_skip, ARG_UNUSED an_operand *simple_result, a_dynamic_init_ptr *p_dip, an_expr_node_ptr *p_temp_init_node, a_source_position *closing_paren_position) /* Scan and process the argument list for a C++ constructor call. The current token is the one right after the opening parenthesis of the argument list. The constructor symbol (possibly overloaded) is constructor_sym. Scan the arguments and the closing parenthesis, and return a pointer to a dynamic initialization entry for the constructor call in *p_dip (*p_dip is set to NULL for an error). If p_temp_init_node is non-NULL, create an enk_temp_init node (for the value of a temporary) pointing to that dynamic initialization entry, and return a pointer to it in *p_temp_init_node; dest_type must be provided in that case and is the type for the node. An error node is returned for an error. *source_pos indicates the source position of the call (often, the position of the left parenthesis). object_class_type is the type of the object being constructed, which may be different than the type of the constructor being called (e.g., when a base class constructor is being called for a derived class object); if it's NULL, the class of the constructor is assumed. If fill_in_dtor is TRUE, appropriate destruction is placed in the returned dynamic initialization (but not attached to an object lifetime). If elision_allowed is TRUE, a call of a copy constructor can be elided or turned into a bitwise move. If is_custom_ms_attr_arg_list is TRUE, this function is invoked to parse the arguments for a custom "Microsoft COM-style attribute". conv_context describes the conversion implied by the constructor (usually, CCO_DIRECT_INITIALIZATION, but not for some list-initializer cases like "X x = { 1, 2 };"). If trivial_ctor is non-NULL, and the initialization required turns out to be calling a trivial default constructor (which does nothing), and there's no destructor to be called (either because the class doesn't have one or because fill_in_dtor is FALSE), return *trivial_ctor set to TRUE, don't construct a dynamic initialization entry, and return *p_dip set to NULL. Otherwise, return *trivial_ctor set to FALSE if trivial_ctor is non-NULL. If explicit_ctor is non-NULL, set *explicit_ctor to TRUE if the constructor to be called has been marked as explicit, otherwise set *explicit_ctor to FALSE. If elision_done is non-NULL, set *elision_done to indicate whether or not copy elision was done. If unboxing_conv is non-NULL, and there is a single argument and its conversion to the class type is a C++/CLI unboxing conversion, return *unboxing_conv set to TRUE, return the argument in *simple_result, don't construct a dynamic initialization entry, and return *p_dip set to NULL. If string_ctor_skip is non-NULL, and there is a single argument of C++/CLI type System::String, return *string_ctor_skip set to TRUE, return the argument in *simple_result, don't construct a dynamic initialization entry, and return *p_dip set to NULL. If closing_paren_position is non-NULL, *closing_paren_position is set to the source position of the closing parenthesis (but it's not set on a rescan). This routine may be called only in C++ mode. It's used for parenthesis- enclosed initializers for classes that have constructors, as in class A {...}; A x(1, 2, 3); The caller need not add the right parenthesis to the stop tokens set, or remove it later, as this routine takes care of that. If rcblock is non-NULL, redo semantic analysis on a previously-scanned expression, and return the result as usual (or an error indication in *rcblock). If arg_list_supplied is TRUE, a third interface alternative: the possibly-empty list of arguments is supplied by supplied_arg_list, and no source is scanned. supplied_arg_list is not freed by this routine. In addition, if init_list_ctor_arg_list is non-NULL, it points to a braced-init-list that we should try to match as a single argument for an initializer-list constructor (see [over.match.list] in the C++11 standard) in addition to trying to match supplied_arg_list in the usual way. In that case, supplied_arg_list will be equal to init_list_ctor_arg_list->variant.braced.list. */ { a_boolean overloaded_function_case = FALSE; a_boolean unknown_dependent_ctor = FALSE; a_boolean is_trivial_construction = FALSE; a_boolean is_bitwise_copy = FALSE; a_boolean is_temp_after_conv = FALSE; a_boolean optimized = FALSE; a_boolean value_init = FALSE; a_routine_ptr routine = NULL; a_type_ptr routine_type = NULL, class_type; a_class_symbol_supplement_ptr cssp; an_arg_list_elem_ptr arg_list = NULL, eff_arg_list; an_arg_match_summary_ptr arg_match_list = NULL; an_expr_node_ptr arg_expr_list; a_dynamic_init_ptr dip = NULL; an_expr_node_ptr temp_init_node = NULL; #if MICROSOFT_EXTENSIONS_ALLOWED a_boolean unboxing_conv_should_be_tried = FALSE; a_boolean literal_case = FALSE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ a_boolean init_list_ctor_case = FALSE; a_boolean saved_allow_call_with_incomplete_return_type; a_boolean saved_in_call_argument; a_boolean force_dependence = FALSE; db_enter(4, "scan_ctor_arguments"); check_assertion(init_list_ctor_arg_list == NULL || (arg_list_supplied && is_braced_init_component(init_list_ctor_arg_list) && init_list_ctor_arg_list->variant.braced.list == supplied_arg_list)); saved_in_call_argument = expr_stack->in_call_argument; expr_stack->in_call_argument = TRUE; /* Allowing a call with incomplete return type doesn't propagate to calls in the arguments of the current call. */ saved_allow_call_with_incomplete_return_type = expr_stack->allow_call_with_incomplete_return_type; expr_stack->allow_call_with_incomplete_return_type = FALSE; /* Initialize returned flags: */ if (trivial_ctor != NULL) *trivial_ctor = FALSE; if (explicit_ctor != NULL) *explicit_ctor = FALSE; if (elision_done != NULL) *elision_done = FALSE; if (unboxing_conv != NULL) *unboxing_conv = FALSE; if (string_ctor_skip != NULL) *string_ctor_skip = FALSE; class_type = sym_parent_class(constructor_sym); cssp = symbol_supplement_for_class(class_type); /* If the object_class_type is not specified, use the default. */ if (object_class_type == NULL) object_class_type = class_type; #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && unboxing_conv != NULL && is_value_class_type(class_type)) { /* The unboxing conversion is "overloaded" with whatever constructors there are. */ overloaded_function_case = TRUE; unboxing_conv_should_be_tried = TRUE; } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here. */ if (constructor_sym->kind == (a_symbol_kind)sk_member_function) { /* Constructor is not overloaded. In this case, the argument types can be checked as the argument list is scanned. */ routine_type = routine_symbol_type(constructor_sym); routine = constructor_sym->variant.routine.ptr; if (is_copy_constructor(routine, (a_type_ptr)NULL, (a_type_qualifier_set *)NULL, /*include_move_ctors=*/TRUE, /*is_declarative_context=*/FALSE)) { /* The sole constructor is a copy constructor. We may be able to elide the call, so go through the processing route for overloaded functions so that we will not convert the operand to the parameter type and then have to unconvert it later to do the optimization. */ overloaded_function_case = TRUE; routine_type = NULL; routine = NULL; } /* if */ } else { check_assertion_str( constructor_sym->kind == (a_symbol_kind)sk_overloaded_function || constructor_sym->kind == (a_symbol_kind)sk_function_template, "scan_ctor_arguments: sym not function"); /* Constructor is overloaded or a template. */ overloaded_function_case = TRUE; } /* if */ /* Scan the arguments. */ if (rcblock == NULL && !arg_list_supplied && (curr_token != tok_rparen || cached_initializer_present()) && !strict_ansi_mode && scope_stack_top().in_prototype_instantiation && scope_stack_top().in_variadic_template) { /* Consider: template int g() { [](auto...ps) { new T(As(ps)...); }; return 1; } struct S { S(int); }; int r = g(); This is technically invalid because As is empty, so T(As(ps)...) becomes S(), and S has no default constructor. However, MSVC, GCC, and Clang all accept such examples. To emulate that, we'll force overload resolution of S() to be treated as dependent in such cases. We start by tentatively setting a flag assuming dependence is needed if we see a syntactically non-empty argument list in a variadic prototype instantiation. */ force_dependence = TRUE; } /* if */ scan_call_arguments(routine_type, routine, tok_rparen, /*already_after_left_delim=*/TRUE, &arg_expr_list, overloaded_function_case, /*p_unknown_dependent_function=*/NULL, /*args_will_be_discarded=*/FALSE, is_custom_ms_attr_arg_list, rcblock, arg_list_supplied, supplied_arg_list, &arg_list, (an_operand *)NULL, (a_boolean *)NULL, closing_paren_position); if (force_dependence && (arg_list != NULL || arg_expr_list != NULL)) { /* See above for the initial setting of force_dependence. If actual arguments were determined, this is not an empty pack expansion and we need not force template dependence. */ force_dependence = FALSE; } /* if */ eff_arg_list = arg_list; error_position = *source_pos; if (value_initialization_enabled && (overloaded_function_case ? (arg_list == NULL) : (arg_expr_list == NULL))) { /* Empty parentheses ("()") indicate value-initialization. That also includes a case like (x...) where a pack expansion expands to zero expressions. */ value_init = TRUE; } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (microsoft_mode && is_single_elem(arg_list) && is_expression_component(arg_list)) { an_operand *opnd = operand_of_arg_list_elem(arg_list); if (unboxing_conv_should_be_tried && unboxing_conversion_possible(opnd->type, class_type, (a_std_conv_descr *)NULL)) { /* There's one argument, and its conversion to the class type is a C++/CLI unboxing conversion. Return the operand to the caller. */ *unboxing_conv = TRUE; copy_operand(operand_of_arg_list_elem(arg_list), simple_result); goto end_of_routine; } else if (string_ctor_skip != NULL && (f_identical_types(opnd->type, make_handle_to_system_string(), ITF_NO_FLAGS) || /*lint !e820*/ (literal_case = is_literal_convertible_to_cli_string( opnd, /*allow_complex=*/TRUE)))) { /* There's one argument, of type System::String^, and we've been asked to handle that specially. Return the operand to the caller. This is used to handle a gcnew of a C++/CLI String type. If the argument is a String^, no gcnew is done; the argument is simply returned to the caller. Note that this also applies to string literals that can be converted to System::String (that was really the reason for this "optimization", but MSVC didn't do enough of a check and let other non-literal String cases by as well). */ *string_ctor_skip = TRUE; copy_operand(opnd, simple_result); if (literal_case) convert_operand_to_handle_to_cli_string(simple_result); goto end_of_routine; } else if (elision_allowed && microsoft_bugs && microsoft_version < 1900) { /* At least in some cases, elision is decided early on by Microsoft compilers. In particular, if the generation of the copy constructor was suppressed (a Microsoft-only feature/bug), initialization from a temporary still succeeds even if there is no other way to perform the copy. Starting with version 19.00 (tested with an early "CTP2" preview version), it appears Microsoft compilers are now much closer to standard behavior. */ if (!cssp->has_user_provided_copy_constructor && cssp->has_copy_constructor_for_const_object && identical_types(opnd->type, class_type) && is_temp_init_usable_in_optimization(opnd, !fill_in_dtor, &temp_init_node, p_dip)) { if (p_temp_init_node != NULL) *p_temp_init_node = temp_init_node; goto end_of_routine; } /* if */ } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (overloaded_function_case) { if (!(conv_context & CCO_DIRECT_INITIALIZATION)) { conv_context |= CCO_IGNORE_EXPLICIT_MEMBERS; } /* if */ if (force_dependence) { /* We determined above that the constructor invocation should be treated as dependent (thereby avoiding diagnostics in prototype instantiations that other implementations do not issue). */ conv_context |= CCO_FORCE_DEPENDENCE; } /* if */ /* The constructors are overloaded. Select the proper one. */ /* Note that a special case allows passing have_selector == TRUE and NULL for the selector operand when dealing with constructors. */ constructor_sym = select_overloaded_function( constructor_sym, /*is_template_id=*/FALSE, (a_template_arg_ptr*)NULL, /*have_selector=*/TRUE, (an_operand *)NULL, arg_list, init_list_ctor_arg_list, conv_context, /*do_arg_dep_lookup=*/FALSE, /*use_pure_arg_dep_lookup=*/FALSE, /*use_std_for_arg_dep_lookup=*/FALSE, oc_constructor, source_pos, (a_token_sequence_number)0, (a_boolean *)NULL, &init_list_ctor_case, &unknown_dependent_ctor, (a_boolean *)NULL, (a_symbol_ptr *)NULL, &arg_match_list); } /* if */ if (constructor_sym != NULL) { /* No error; we know which constructor is to be called. */ an_arg_match_summary_ptr arg_match = NULL; a_type_ptr param_type, source_type = NULL; /* If we matched an initializer-list constructor, the effective argument list is just the single braced-init-list. */ if (init_list_ctor_case) eff_arg_list = init_list_ctor_arg_list; routine = constructor_sym->variant.routine.ptr; if (explicit_ctor != NULL) { *explicit_ctor = routine->is_explicit_constructor; } /* if */ if (routine->is_trivial_default_constructor && eff_arg_list == NULL) { /* The constructor selected is a trivial default constructor, which does nothing. The routine is not marked as called. */ is_trivial_construction = TRUE; expr_reference_to_implicitly_invoked_function_full( constructor_sym, source_pos, object_class_type, /*honor_virtual=*/FALSE, /*evaluated=*/FALSE, /*instantiate=*/FALSE); optimized = TRUE; } else if (elision_allowed && is_copy_constructor(routine, (a_type_ptr)NULL, (a_type_qualifier_set *)NULL, /*include_move_ctors=*/TRUE, /*is_declarative_context=*/FALSE) && /* Avoid problems with specified arguments with defaults: */ is_single_elem(eff_arg_list) && (check_assertion(overloaded_function_case), /* Forced above. */ (arg_match = arg_match_list->next), /* Watch out for an ambiguous conversion. */ conv_usable(&arg_match->conversion))) { a_routine_ptr conv_routine; a_type_ptr conv_rout_type; /* The constructor selected is a copy constructor, so certain optimizations may be possible. */ param_type = routine->type->variant.routine.extra_info->param_type_list->type; source_type = type_pointed_to(param_type); if (is_expression_component(eff_arg_list) && is_braced_init_list_operand(operand_of_arg_list_elem(eff_arg_list))){ eff_arg_list = operand_of_arg_list_elem(eff_arg_list) ->variant.braced_init_list; } /* if */ if (!is_expression_component(eff_arg_list)) { check_assertion(is_braced_init_component(eff_arg_list)); /* The parameter of the copy constructor, of type reference to class, has been bound to a braced-init-list, which means a temporary of the class type will be created. So the optimization is possible. */ handle_elided_copy_constructor(source_type, routine, source_pos); optimized = TRUE; } else if (is_null_user_conv_descr(&arg_match->conversion) && f_same_entities(f_skip_typerefs( operand_of_arg_list_elem(eff_arg_list)->type), class_type) && is_temp_init_usable_in_optimization( operand_of_arg_list_elem(eff_arg_list), /*suppress_dtor=*/!fill_in_dtor, &temp_init_node, &dip)) { /* The source operand for the copy is itself a temporary of the same cv-unqualified type as the destination, so copy constructor elision can be done. In this case the dynamic init entry already present is reused. */ /* Clear the explicit cast flag. The caller will set it if this top-level operation is an explicit cast. We've optimized away the explicit cast one level down. However, don't clear the flag when initializing a variable: The C++-generating back end uses it to reproduce the source form, which may be needed with deduced types. E.g.: using X = std::tuple; std::tuple x(X(nullptr, 42)); The cast here must be rendered to deduce the correct type for x (i.e., std::tuple); if it were rendered std::tuple x(nullptr, 42); the deduced type would be std::tuple. */ if (init_list_ctor_arg_list == NULL && !(conv_context & CCO_INITIALIZING_VARIABLE)) { dip->is_explicit_cast = FALSE; } /* if */ handle_elided_copy_constructor(source_type, routine, source_pos); optimized = TRUE; } else if ((conv_routine = arg_match->conversion.routine) != NULL && (!(microsoft_bugs && microsoft_version < 1933) || special_kind_is(conv_routine, sfk_constructor)) && ((special_kind_is(conv_routine, sfk_constructor) && f_same_entities(parent_class_of(conv_routine), class_type)) || (special_kind_is(conv_routine, sfk_conversion) && ((conv_rout_type = f_skip_typerefs(conv_routine->type)), f_same_entities(skip_typerefs(conv_rout_type-> variant.routine.return_type), class_type) && rout_type_supp(conv_rout_type) ->value_returned_by_cctor)))) { /* The argument will be a temporary of the right type after the user-defined conversion indicated for the argument is applied, and elision can be done on that. */ /* MSVC++ prior to version 19.33 doesn't do this optimization if the user-defined conversion calls a conversion operator (as opposed to a constructor). */ is_temp_after_conv = TRUE; arg_match->conversion.class_object_adjustment_required = FALSE; optimized = TRUE; } else if (routine->is_trivial_copy_function) { /* The constructor selected is a bitwise copy constructor. A copy can be used instead of a call. The routine is not marked as called. */ is_bitwise_copy = TRUE; if (mandatory_copy_elision && is_a_prvalue(operand_of_arg_list_elem(eff_arg_list)) && special_kind_is(routine, sfk_constructor)) { /* Under mandatory copy elision rules (C++17), an elided constructor is not considered at all. */ } else { expr_reference_to_implicitly_invoked_function_full( constructor_sym, source_pos, object_class_type, /*honor_virtual=*/FALSE, /*evaluated=*/FALSE, /*instantiate=*/FALSE); } /* if */ optimized = TRUE; } /* if */ } /* if */ if (optimized) { /* Optimized cases -- extract the argument as an expression, doing required user-defined conversions but not any final conversion that would be added by adjust_overloaded_function_call_arguments merely to make the argument match the copy constructor's parameter type (e.g., addition of "const" for a reference-to-const parameter type). */ if (elision_done != NULL) *elision_done = TRUE; /* Since this call is elided, we should really process the argument as if it is not an argument of the current call. */ expr_stack->in_call_argument = saved_in_call_argument; if (is_trivial_construction) { /* Call of a trivial default constructor. No argument list, and the reference to the constructor has already been recorded. */ check_assertion(eff_arg_list == NULL); } else if (is_braced_init_component(eff_arg_list)) { /* For a braced-init-list argument, convert the list to the underlying type of the first parameter. */ an_init_state init_state; expr_clear_init_state(&init_state); init_state.force_dynamic_init = TRUE; prep_list_initializer(eff_arg_list, source_type, /*is_direct_init=*/FALSE, /*check_narrowing=*/TRUE, /*warning_on_narrowing=*/FALSE, CCO_DEFAULT, fill_in_dtor, /*force_temp=*/TRUE, /*make_lvalue_temp=*/FALSE, (an_operand *)NULL, &init_state, (an_arg_match_summary *)NULL); dip = init_state.init_dip; check_assertion(dip != NULL); } else { /* Note that the code here also does not append default argument values as adjust_overloaded_function_call_arguments would. */ an_operand *operand; check_assertion(is_expression_component(eff_arg_list)); operand = operand_of_arg_list_elem(eff_arg_list); check_assertion(overloaded_function_case && arg_match != NULL); if (is_null_user_conv_descr(&arg_match->conversion)) { arg_match->conversion.class_object_adjustment_required = TRUE; } /* if */ if (is_bitwise_copy) { arg_match->conversion.result_is_a_glvalue = FALSE; } /* if */ user_convert_operand(operand, class_type, &arg_match->conversion, (a_conv_descr *)NULL, /*force_copy_to_temp=*/FALSE); if (is_temp_after_conv && !is_error_operand(operand)) { /* We determined previously that the result after the conversion would be a temp we could reuse. This temporary may have already been folded into a constant (e.g., if the conversion function is constexpr). */ if (is_constant_operand(operand)) { a_constant_ptr con = local_constant(); dip = alloc_dynamic_init((a_dynamic_init_kind)dik_constant); extract_constant_from_operand(operand, con); set_dynamic_init_constant(dip, move_local_constant_to_il(&con)); } else { (void)is_temp_init_usable_in_optimization( operand, /*suppress_dtor=*/!fill_in_dtor, &temp_init_node, &dip); handle_elided_copy_constructor(source_type, /*elided_cctor=*/(a_routine *)NULL, source_pos); } /* if */ /* The dynamic init we now have is the result of the overall operation. */ check_assertion(dip != NULL); } else { arg_expr_list = make_node_from_operand(operand); } /* if */ } /* if */ free_arg_match_summary_list(arg_match_list); if (pending_consteval_failure.routine != NULL) { /* An argument contained a call to a consteval function that did not produce a constant but this enclosing "call" could potentially make that okay. Since the call ends up being elided, re-perform the constant-evaluation-failure check: It might result in an actual diagnostic (or substitution failure) or we may defer the decision again for another enclosing call. */ a_routine_ptr consteval_routine = pending_consteval_failure.routine; pending_consteval_failure.routine = NULL; (void)consteval_failure(consteval_routine, (a_constant_ptr)NULL, &pending_consteval_failure.diag_pos, &pending_consteval_failure.diag_list); } /* if */ } else { /* Normal, not-optimized case. The argument list will be processed in the normal way below. */ /* Check that the constructor is accessible and mark it referenced. */ expr_reference_to_implicitly_invoked_function(constructor_sym, source_pos, object_class_type, /*honor_virtual=*/FALSE); } /* if */ } /* if */ if (overloaded_function_case && !optimized) { /* Build an expression-form argument list. Convert the arguments on the argument list to the right types. The call is done even when constructor_sym is NULL because it also frees arg_match_list. */ /* Again, note that a special case allows passing have_selector == TRUE and NULL for the selector operand when dealing with constructors. */ adjust_overloaded_function_call_arguments(constructor_sym, unknown_dependent_ctor, (a_type_ptr)NULL, /*have_selector=*/TRUE, (an_operand *)NULL, eff_arg_list, arg_match_list, &arg_expr_list); } /* if */ if (dip == NULL) { /* Make the dynamic initialization entry for the call. */ if (constructor_sym == NULL && !unknown_dependent_ctor) { /* Error. */ /* dip = NULL; -- already set. */ } else { if (is_custom_ms_attr_arg_list) { /* Construction of a custom Microsoft attribute. */ dip = alloc_expr_dynamic_init((a_dynamic_init_kind)dik_constructor); dip->variant.constructor.ptr = routine; /* Possibly NULL. */ dip->variant.constructor.args = arg_expr_list; dip->variant.constructor.value_initialization = value_init; } else if (is_bitwise_copy) { /* Bitwise copy construction of a class. */ dip = alloc_expr_dynamic_init((a_dynamic_init_kind)dik_expression); dip->variant.expression = arg_expr_list; } else if (is_trivial_construction) { /* Trivial construction, which does nothing (or zeroing, for value initialization). */ if (value_init) { dip = alloc_expr_dynamic_init((a_dynamic_init_kind)dik_zero); } else if (trivial_ctor != NULL && !(fill_in_dtor && has_nontrivial_destructor(cssp))) { /* The caller has given us a way of indicating this case, instead of creating a dik_none dynamic initialization. Note that we can't do this if a destruction needs to be indicated. */ *trivial_ctor = TRUE; /* dip = NULL; -- already set. */ } else { dip = alloc_expr_dynamic_init((a_dynamic_init_kind)dik_none); } /* if */ } else { /* Constructor call. This may be folded in the case of constexpr constructors. Contexts that disallow elision happen to also be contexts that disallow folding (e.g., in the case of a new expression that relies on the constructor call to call the allocation function). */ /* We're now processing the call instead of the call arguments. So restore the expression stack accordingly. */ expr_stack->in_call_argument = saved_in_call_argument; dip = alloc_expr_ctor_dynamic_init(routine, arg_expr_list, dest_type, /*static_temp=*/FALSE, /*add_default_args=*/FALSE, /*implied_source=*/FALSE, value_init, /*sequenced_args=*/ (init_list_ctor_arg_list != NULL), /*fold_constexpr=*/elision_allowed, /*check_constexpr=*/elision_allowed, source_pos); if (is_error_dynamic_init(dip)) { /* Some error. */ dip = NULL; } else if (dyn_init_is(dip, dik_constant) && (conv_context & CCO_INITIALIZING_VARIABLE) != 0) { /* The constructor call was folded to a constant. This may have produced an enk_temp_init backing expression, but for the variable initializer case it should be an enk_initializer instead. */ an_expr_node *node = dip->variant.constant.ptr->expr; if (node != NULL && node_is(node, enk_temp_init)) { a_dynamic_init *orig_dip = node->variant.init.dynamic_init; node->kind = enk_initializer; node->variant.initializer.dyn_init = orig_dip; if (init_list_ctor_arg_list != NULL && is_braced_init_component(init_list_ctor_arg_list)) { orig_dip->is_braced_initializer = TRUE; } /* if */ } /* if */ } /* if */ } /* if */ if (fill_in_dtor && dip != NULL) { /* Fill in the destructor information. Note that we cannot use alloc_dtor_dynamic_init because it does not allow for the object_class_type to differ from the class_type. */ add_dtor_to_dynamic_init(dip, class_type, object_class_type, source_pos); } /* if */ } /* if */ } /* if */ *p_dip = dip; /* Build an enk_temp_init node if one is needed and one did not exist already. */ if (p_temp_init_node != NULL) { /* Note that the code here is very similar to the code at the end of determine_dynamic_init_for_class_init. */ if (trivial_ctor != NULL && *trivial_ctor) { temp_init_node = NULL; } else if (temp_init_node == NULL) { if (dip == NULL) { /* Some error. Return an error node. */ temp_init_node = error_node(); } else { temp_init_node = alloc_temp_init_node(dest_type, dip, /*is_lvalue=*/FALSE, /*is_explicit_cast=*/FALSE); } /* if */ } else { /* Existing enk_temp_init; make sure we get a prvalue for the temporary instead of a glvalue. Note that in this case dip is the dynamic init pointer extracted from that node. */ if (is_glvalue_node(temp_init_node)) { temp_init_node = rvalue_expr_for_lvalue(temp_init_node); } /* if */ temp_init_node->type = dest_type; /* Put the dynamic initialization on a destruction list if appropriate. */ set_temp_init_dynamic_init_lifetime(temp_init_node); } /* if */ *p_temp_init_node = temp_init_node; } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED end_of_routine: #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (arg_list != NULL && arg_list != supplied_arg_list) { free_arg_list(arg_list); } /* if */ expr_stack->in_call_argument = saved_in_call_argument; /* Restore the previous state wrt. allowing an incomplete return type for the current call. */ expr_stack->allow_call_with_incomplete_return_type = saved_allow_call_with_incomplete_return_type; db_exit(); } /* scan_ctor_arguments */ static an_expr_node_ptr scan_va_list_operand(a_boolean value_used, an_error_code err_code, a_boolean *err) /* Scan an expression that is a va_list operand of a stdarg operation and return a pointer to it. Check that the expression is an lvalue of the builtin type va_list from , or a prvalue of that type decayed to a pointer if va_list is an array type. If the type or value category is wrong, issue the error err_code, set *err to TRUE, and return NULL. For the non-array case, the value of the lvalue is used if value_used is TRUE; the lvalue is assumed always to be set (since we don't know what the underlying implementation is). */ { an_operand operand; an_expr_node_ptr node; a_local_expr_options_set local_options = TOPT_NO_OPTIONS; a_boolean array_va_list = FALSE; a_type_ptr eff_va_list_type; check_assertion(builtin_va_list_type != NULL); eff_va_list_type = builtin_va_list_type; if (is_array_type(builtin_va_list_type)) { array_va_list = TRUE; eff_va_list_type = type_after_array_to_pointer_transformation(eff_va_list_type); } else { local_options = TOPT_SUPPRESS_ARRAY_TO_POINTER_CONVERSION | TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION; } /* if */ scan_expr(&operand, PREC_LOWEST, EOPT_DISALLOW_COMMA_OPERATOR); do_operand_transformations(&operand, local_options); /* The operand must be an lvalue of the builtin type va_list (or a prvalue of that type decayed to a pointer, if va_list is an array type. */ if ((array_va_list ? !is_a_prvalue(&operand) : !is_an_lvalue(&operand)) || (!is_template_dependent_type(operand.type) && !types_are_compatible_ignoring_qualifiers(eff_va_list_type, operand.type))) { if (!is_error_operand(&operand)) { error_in_operand(err_code, &operand); } /* if */ *err = TRUE; } /* if */ if (!*err) { if (!array_va_list) modifying_lvalue(&operand, value_used); node = make_node_from_operand(&operand); } else { operand_will_not_be_used_because_of_error(&operand); node = NULL; } /* if */ return node; } /* scan_va_list_operand */ static void scan_va_start_operator(an_operand *result, an_operand *builtin_func, a_boolean single_operand) /* Scan a reference to the or va_start macro, when it is treated as a builtin. The form is expected when single_operand is FALSE: va_start(va_list_var, last_param) where va_list_var is a variable declared with the builtin type va_list, and last_param is the last parameter before the "..." of the function. When single_operand is TRUE, the form is expected: va_start(va_list_var) If builtin_func is non-NULL, the construct is being handled as a pseudo-call to a builtin function and the function name and left parenthesis have already been scanned: builtin_func represents the reference to the builtin function (this is how GNU's __builtin_va_start is handled). */ { a_source_position start_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ an_operand operand; an_expr_node_ptr node1, node2; a_boolean err = FALSE; a_variable_ptr last_param_var = NULL; db_enter(4, "scan_va_start_operator"); /* va_start not possible in preprocessing expressions. */ check_assertion_str(!curr_expr_kind_is(ek_pp), "scan_va_start_operator: in preprocessing expr"); if (builtin_func != NULL) { /* A __builtin_va_start call: The function name and left parenthesis has already been scanned. */ start_position = builtin_func->position; } else { /* The current token is a va_start keyword. */ /* Save the position of the va_start keyword. */ start_position = pos_curr_token; /* Advance past va_start. */ (void)get_token(); /* Check for and pass over the left parenthesis. */ (void)required_token(tok_lparen, ec_exp_lparen); add_matching_stop_token(tok_rparen); } /* if */ if (curr_expr_kind_is_traditional_const()) { /* va_start is not allowed in constant expressions. */ expr_pos_error(ec_bad_constant_operator, &start_position); err = TRUE; } else if (operator_not_allowed_in_cpp11_constant_expr(&start_position)) { /* C++11 constant expressions do not allow va_start (it sets the operand variable). */ err = TRUE; } /* if */ if (!err) { /* Check if we are in a valid function for the use of va_start. */ a_boolean bad_scope = TRUE; if (depth_innermost_function_scope != NO_SCOPE_DEPTH) { a_routine_ptr routine = scope_stack[depth_innermost_function_scope].assoc_routine; a_type_ptr routine_type = skip_typerefs(routine->type); if (routine_type->variant.routine.extra_info->has_ellipsis) { bad_scope = FALSE; /* Find the last parameter variable so we can check that it is the one referenced in the second operand. */ last_param_var = innermost_function_scope->variant.routine.parameters; if (last_param_var == NULL) { if (gpp_mode) { /* g++ allows "this" to be considered as a parameter. */ last_param_var = innermost_function_scope->variant.routine .this_param_variable; } /* if */ if (last_param_var == NULL) bad_scope = TRUE; } else { while (last_param_var->next != NULL) { last_param_var = last_param_var->next; } /* while */ } /* if */ } /* if */ } /* if */ if (bad_scope) { expr_pos_error(ec_va_start_requires_ellipsis_function, &start_position); err = TRUE; } /* if */ } /* if */ if (!single_operand) { add_stop_token(tok_comma); } /* if */ /* Scan the first expression. */ node1 = scan_va_list_operand(/*value_used=*/FALSE, ec_bad_va_start, &err); if (!single_operand) { /* Check for and pass over the comma. */ add_stop_token(tok_identifier); (void)required_token(tok_comma, ec_exp_comma); remove_stop_token(tok_identifier); remove_stop_token(tok_comma); /* Scan the second expression. */ scan_expr(&operand, PREC_LOWEST, EOPT_DISALLOW_COMMA_OPERATOR); do_operand_transformations(&operand, TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION); /* The expression must be a parameter of the function. Note that the is_parameter test below could be true for a case like the following: void f(int i, ...) { struct L { void m(int j, ...) { va_list ap; va_start(ap, i); } }; } but in such cases an error will have been issued earlier about an invalid reference to a variable of the enclosing function. If the named parameter is a reference, we do not accept the code in most modes (the standard makes it undefined behavior), but we do accept it when emulating recent GNU C++ compilers. */ if (is_expression_operand(&operand)) { a_boolean okay = FALSE; node2 = operand.variant.expression; if (gpp_mode && gnu_version >= 30200) { /* Strip a reference indirection in g++ mode so we can check what's underneath. */ node2 = strip_ref_indirect(node2, /*parens_also=*/TRUE); } /* if */ node2 = skip_parens(node2); if (gpp_mode && is_operation_node(node2) && node_operator_is(node2, eok_cast) && interchangeable_types(node2->type, node2->variant.operation.operands->type)) { /* g++ allows a mostly-harmless cast. (Actually, g++ seems to allow pretty much anything, but we're trying to limit our acceptance to sort-of-okay cases.) */ node2 = node2->variant.operation.operands; node2 = skip_parens(node2); } /* if */ if (is_variable_node(node2) && (node2->is_lvalue || gpp_mode)) { if (last_param_var == NULL) { /* Previous error. */ check_assertion(err); } else if (node_variable(node2) == last_param_var) { /* Correct use of the final parameter. */ okay = TRUE; } else if (node_variable(node2)->is_parameter) { /* A parameter, but not the last one. Call that a warning. */ pos_warning(ec_bad_va_start, &operand.position); okay = TRUE; } /* if */ } /* if */ if (!okay && !err) { /* Other weird cases. GNU calls these warnings, presumably because it knows the right parameter to name. */ if (gnu_mode) { pos_warning(ec_bad_va_start, &operand.position); okay = TRUE; } else { error_in_operand(ec_bad_va_start, &operand); err = TRUE; } /* if */ } /* if */ if (!err) { #if BUILTIN_VA_START_TAKES_ADDRESS_OF_VARIABLE /* Many implementations of va_start expose the address of the parameter variable. Also consider this a use of the parameter. */ change_ref_kinds(operand.ref_entries_list, SRK_USE | SRK_ADDRESS_TAKEN); #endif /* BUILTIN_VA_START_TAKES_ADDRESS_OF_VARIABLE */ node1->next = node2; } /* if */ } else if (!err) { if (gnu_mode && builtin_func != NULL && is_constant_operand(&operand)) { node1->next = make_node_from_operand(&operand); pos_warning(ec_bad_va_start, &operand.position); } else { if (!is_error_operand(&operand)) { error_in_operand(ec_bad_va_start, &operand); } /* if */ err = TRUE; } /* if */ } /* if */ } /* if */ if (err) { make_error_operand(result); } else { /* Create a va_start expression node. */ an_expr_node_ptr va_start_node; va_start_node = make_operator_node((an_expr_operator_kind)(single_operand ? (an_expr_operator_kind)eok_va_start_single_operand : (an_expr_operator_kind)eok_va_start), void_type(), node1); make_expression_operand(va_start_node, result); } /* if */ rule_out_expr_kinds(ROEK_CONSTANT, result); if (builtin_func == NULL) { #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* Check for and pass over the right parenthesis. */ (void)required_token(tok_rparen, ec_exp_rparen); remove_matching_stop_token(tok_rparen); set_operand_position(result, &start_position, &end_position, &start_position); } /* if */ db_exit(); } /* scan_va_start_operator */ static void scan_va_arg_operator(an_operand *result, an_operand *builtin_func) /* Scan a reference to the va_arg macro, when it is treated as a builtin. Its form is va_arg(va_list_var, type) where va_list_var is a variable declared with the builtin type va_list, and type is the type of the argument to be extracted. If builtin_func is non-NULL, the construct is being handled as a pseudo-call to a builtin function and the function name and left parenthesis have already been scanned: builtin_func represents the reference to the builtin function (this is how GNU's __builtin_va_arg is handled). */ { a_source_position start_position, type_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ an_expr_node_ptr node; a_type_ptr type, type_to_cast_to = NULL; a_boolean err = FALSE; db_enter(4, "scan_va_arg_operator"); /* va_arg not possible in preprocessing expressions. */ check_assertion_str(!curr_expr_kind_is(ek_pp), "scan_va_arg_operator: in preprocessing expr"); if (builtin_func != NULL) { /* A __builtin_va_arg call: The function name and left parenthesis has already been scanned. */ start_position = builtin_func->position; } else { /* Save the position of the va_arg keyword. */ start_position = pos_curr_token; /* Advance past va_arg. */ (void)get_token(); /* Check for and pass over the left parenthesis. */ (void)required_token(tok_lparen, ec_exp_lparen); add_matching_stop_token(tok_rparen); } /* if */ if (curr_expr_kind_is_traditional_const()) { /* va_arg is not allowed in constant expressions. */ expr_pos_error(ec_bad_constant_operator, &start_position); err = TRUE; } else if (operator_not_allowed_in_cpp11_constant_expr(&start_position)) { /* C++11 constant expressions do not allow va_arg (it updates the parameter list tracking variable). */ err = TRUE; } /* if */ add_stop_token(tok_comma); /* Scan the expression. */ node = scan_va_list_operand(/*value_used=*/TRUE, ec_bad_va_arg, &err); /* Check for and pass over the comma. */ add_stop_token(tok_identifier); (void)required_token(tok_comma, ec_exp_comma); remove_stop_token(tok_identifier); remove_stop_token(tok_comma); /* Scan the type. */ type_position = pos_curr_token; type_name(&type); if (is_function_type(type) || is_array_type(type) || is_any_reference_type(type)) { /* The type is not allowed to be an array, function, or reference type. */ expr_pos_error(ec_bad_va_arg, &type_position); err = TRUE; } else if (!C_mode() && is_class_struct_union_type(type) && is_nonPOD_or_has_nontrivial_copy_semantics(type)) { /* A C++03 class type must be a POD; in modern C++, it requires trivial copy semantics. Note that this check is needed -- if we don't check this we may try to take the address of a va_arg node to get the object address to call the copy constructor, and we'll get an abort. */ expr_pos_error(ec_non_pod_va_arg, &type_position); err = TRUE; } else if (!va_arg_returns_lvalue) { a_type_ptr promoted_type = default_argument_promotion(type); if (!identical_types(type, promoted_type)) { /* The type must possibly be obtained after default promotion. */ expr_pos_ty2_diagnostic(es_warning, ec_va_arg_would_have_been_promoted, &type_position, type, promoted_type); type_to_cast_to = type; type = promoted_type; } /* if */ } /* if */ if (err) { make_error_operand(result); } else if (va_arg_returns_lvalue) { /* Create an lvalue va_arg expression node for an lvalue. */ an_expr_node_ptr va_arg_node = make_lvalue_operator_node( (an_expr_operator_kind)eok_va_arg, type, node); make_glvalue_expression_operand(va_arg_node, result); } else { /* Create a va_arg expression node for a prvalue. */ an_expr_node_ptr va_arg_node = make_operator_node((an_expr_operator_kind)eok_va_arg, type, node); if (type_to_cast_to != NULL) { cast_node(&va_arg_node, type_to_cast_to, /*check_cast_access=*/TRUE, /*check_ambiguity=*/TRUE, /*is_implicit_cast=*/FALSE, /*is_reinterpret_cast=*/FALSE, /*reinterpret_semantics=*/FALSE, /*within_expr_processing=*/TRUE, &start_position); } /* if */ make_expression_operand(va_arg_node, result); } /* if */ rule_out_expr_kinds(ROEK_CONSTANT, result); if (builtin_func == NULL) { #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* Check for and pass over the right parenthesis. */ (void)required_token(tok_rparen, ec_exp_rparen); remove_matching_stop_token(tok_rparen); set_operand_position(result, &start_position, &end_position, &start_position); } /* if */ db_exit(); } /* scan_va_arg_operator */ static void scan_va_end_operator(an_operand *result, an_operand *builtin_func) /* Scan a reference to the va_end macro, when it is treated as a builtin. Its form is va_end(va_list_var) where va_list_var is a variable declared with the builtin type va_list. If builtin_func is non-NULL, the construct is being handled as a pseudo-call to a builtin function and the function name and left parenthesis have already been scanned: builtin_func represents the reference to the builtin function (this is how GNU's __builtin_va_end is handled). */ { a_source_position start_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ an_expr_node_ptr node; a_boolean err = FALSE; db_enter(4, "scan_va_end_operator"); /* va_end not possible in preprocessing expressions. */ check_assertion_str(!curr_expr_kind_is(ek_pp), "scan_va_end_operator: in preprocessing expr"); if (builtin_func != NULL) { /* A __builtin_va_end call: The function name and left parenthesis has already been scanned. */ start_position = builtin_func->position; } else { /* Save the position of the va_end keyword. */ start_position = pos_curr_token; /* Advance past va_end. */ (void)get_token(); /* Check for and pass over the left parenthesis. */ (void)required_token(tok_lparen, ec_exp_lparen); add_matching_stop_token(tok_rparen); } /* if */ if (curr_expr_kind_is_traditional_const()) { /* va_end is not allowed in constant expressions. */ expr_pos_error(ec_bad_constant_operator, &start_position); err = TRUE; } else if (operator_not_allowed_in_cpp11_constant_expr(&start_position)) { /* C++11 constant expressions do not allow va_end (it may update the parameter list tracking variable). */ err = TRUE; } /* if */ /* Scan the expression. */ node = scan_va_list_operand(/*value_used=*/TRUE, ec_bad_va_end, &err); if (err) { make_error_operand(result); } else { /* Create a va_end expression node. */ an_expr_node_ptr va_end_node = make_operator_node((an_expr_operator_kind)eok_va_end, void_type(), node); make_expression_operand(va_end_node, result); } /* if */ rule_out_expr_kinds(ROEK_CONSTANT, result); if (builtin_func == NULL) { #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* Check for and pass over the right parenthesis. */ (void)required_token(tok_rparen, ec_exp_rparen); remove_matching_stop_token(tok_rparen); set_operand_position(result, &start_position, &end_position, &start_position); } /* if */ db_exit(); } /* scan_va_end_operator */ static void scan_va_copy_operator(an_operand *result, an_operand *builtin_func) /* Scan a reference to the va_copy macro, when it is treated as a builtin. Its form is va_copy(va_list_dest, va_list_source) where va_list_dest and va_list_source are variables declared with the builtin type va_list. If builtin_func is non-NULL, the construct is being handled as a pseudo-call to a builtin function and the function name and left parenthesis have already been scanned: builtin_func represents the reference to the builtin function (this is how GNU's __builtin_va_copy is handled). */ { a_source_position start_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ an_expr_node_ptr node1, node2; a_boolean err = FALSE; db_enter(4, "scan_va_copy_operator"); /* va_copy not possible in preprocessing expressions. */ check_assertion_str(!curr_expr_kind_is(ek_pp), "scan_va_copy_operator: in preprocessing expr"); if (builtin_func != NULL) { /* A __builtin_va_copy call: The function name and left parenthesis has already been scanned. */ start_position = builtin_func->position; } else { /* Save the position of the va_copy keyword. */ start_position = pos_curr_token; /* Advance past va_copy. */ (void)get_token(); /* Check for and pass over the left parenthesis. */ (void)required_token(tok_lparen, ec_exp_lparen); add_matching_stop_token(tok_rparen); } /* if */ if (curr_expr_kind_is_traditional_const()) { /* va_copy is not allowed in constant expressions. */ expr_pos_error(ec_bad_constant_operator, &start_position); err = TRUE; } else if (operator_not_allowed_in_cpp11_constant_expr(&start_position)) { /* C++11 constant expressions do not allow va_copy (it sets the destination operand variable). */ err = TRUE; } /* if */ add_stop_token(tok_comma); /* Scan the first expression. */ node1 = scan_va_list_operand(/*value_used=*/FALSE, ec_bad_va_copy, &err); /* Check for and pass over the comma. */ add_stop_token(tok_identifier); (void)required_token(tok_comma, ec_exp_comma); remove_stop_token(tok_identifier); remove_stop_token(tok_comma); /* Scan the second expression. */ node2 = scan_va_list_operand(/*value_used=*/TRUE, ec_bad_va_copy, &err); if (err) { make_error_operand(result); } else { /* Create a va_copy expression node. */ an_expr_node_ptr va_copy_node; node1->next = node2; va_copy_node = make_operator_node((an_expr_operator_kind)eok_va_copy, void_type(), node1); make_expression_operand(va_copy_node, result); } /* if */ rule_out_expr_kinds(ROEK_CONSTANT, result); if (builtin_func == NULL) { #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* Check for and pass over the right parenthesis. */ (void)required_token(tok_rparen, ec_exp_rparen); remove_matching_stop_token(tok_rparen); set_operand_position(result, &start_position, &end_position, &start_position); } /* if */ db_exit(); } /* scan_va_copy_operator */ #if BUILTIN_FUNCTIONS_ENABLED static void check_builtin_function_for_call(an_operand *op, a_boolean *foldable, a_boolean *pseudo_call) /* If the given operand corresponds to a built-in function, return through *foldable whether a call to that function might be a valid constant-expression. *pseudo_call is set to TRUE if the arguments to the built-in function call are not treated like standard call arguments (e.g., if they behave like sizeof arguments); otherwise, *pseudo_call is set to FALSE. This routine may also diagnose certain invalid uses of special GNU functions (e.g., uses of __builtin_va_arg_pack in a non-variadic function). */ { a_routine_ptr rp = routine_from_function_operand(op); #if GNU_EXTENSIONS_ALLOWED if (rp != NULL && 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 */ *foldable = *pseudo_call = FALSE; if (rp != NULL && is_gnu_builtin_function(rp)) { *foldable = is_foldable_gnu_builtin_function(rp, pseudo_call); switch (rp->variant.builtin_function_kind) { case bfk_va_arg_pack: case bfk_va_arg_pack_len: if (innermost_function_scope == NULL || !innermost_function_scope->variant.routine.ptr->is_inline || !skip_typerefs(innermost_function_scope->variant.routine.ptr->type) ->variant.routine.extra_info->has_ellipsis) { error_in_operand(ec_bad_function_for_gnu_va_arg_pack, op); } /* if */ break; case bufk_launder: *foldable = TRUE; *pseudo_call = TRUE; break; default: /* No special checks needed. */ break; } /* switch */ } /* if */ } /* check_builtin_function_for_call */ static a_type_class_kind gnu_type_class_for_type(a_type_ptr type) /* Return the GNU type class associated with the given type. (This is used to implement the GNU function __builtin_classify_type.) Different versions of gcc and g++ return slightly different values for some expression types. */ { a_type_class_kind tck = tck_none; type = skip_typerefs(type); switch (type->kind) { case tk_void: tck = (a_type_class_kind)tck_void; break; case tk_integer: if (gpp_mode && gnu_version >= 30400 && is_bool_type(type)) { tck = (a_type_class_kind)tck_bool; } else if (gpp_mode && gnu_version >= 30400 && is_immediate_enum_type(type)) { /* Although there has always been a type class for enum types in GNU C/C++, only g++ 3.4 seems to use it so far. Other GNU compilers return the integer type class instead. */ tck = (a_type_class_kind)tck_enum; } else { tck = (a_type_class_kind)tck_integer; } /* if */ break; case tk_pointer: if (is_pointer_type(type)) { tck = (a_type_class_kind)tck_pointer; } else { /* An expression never has a reference type. */ tck = (a_type_class_kind)tck_reference; unexpected_condition(); } /* if */ break; case tk_ptr_to_member: /* Pointer-to-member types are considered pointer types in earlier g++ versions. */ if (gnu_version < 30400) { tck = (a_type_class_kind)tck_pointer; } else { a_type_ptr member_type = pm_member_type(type); if (!is_function_type(member_type)) { /* A pointer to data member. */ tck = (a_type_class_kind)tck_offset; } else { /* A pointer to function member is treated as a struct type. */ tck = (a_type_class_kind)tck_struct; } /* if */ } /* if */ break; case tk_float: tck = (a_type_class_kind)tck_float; break; #if C99_IL_EXTENSIONS_SUPPORTED case tk_imaginary: case tk_complex: tck = (a_type_class_kind)tck_complex; break; #endif /* C99_IL_EXTENSIONS_SUPPORTED */ case tk_routine: if (gpp_mode && gnu_version >= 30400) { tck = (a_type_class_kind)tck_routine; } else { tck = (a_type_class_kind)tck_pointer; } /* if */ break; case tk_struct: case tk_class: tck = (a_type_class_kind)tck_struct; break; case tk_union: tck = (a_type_class_kind)tck_union; break; case tk_array: if (gpp_mode && gnu_version >= 30400) { tck = (a_type_class_kind)tck_array; } else { tck = (a_type_class_kind)tck_pointer; } /* if */ break; #if FIXED_POINT_ALLOWED case tk_fixed_point: #endif /* FIXED_POINT_ALLOWED */ #if GNU_VECTOR_TYPES_ALLOWED case tk_vector: #endif /* GNU_VECTOR_TYPES_ALLOWED */ case tk_error: case tk_nullptr: tck = (a_type_class_kind)tck_none; break; default: unexpected_condition(); break; } /* switch */ return tck; } /* gnu_type_class_for_type */ a_boolean fold_gnu_call_if_possible(an_operand_ptr op, an_expr_node_ptr call) /* The given operand must represent a function call, and call is the function call node from that call. Some GNU-style __builtin_xxx functions require special compile-time checks and can sometimes be constant- folded. This routine does so and returns TRUE if the call is folded (in which case *op is replaced by a constant operand). A diagnostic may be issued if the arguments are invalid (and *op is replaced by an error operand in such cases). */ { a_boolean folded = FALSE; check_assertion(is_expression_operand(op)); check_assertion(call != NULL && call->kind == (an_expr_node_kind)enk_operation); if (builtin_functions_enabled && call->variant.operation.kind == (an_expr_operator_kind)eok_call) { an_expr_node_ptr args = call->variant.operation.operands; a_routine_ptr routine = routine_from_function_expr(args); args = args->next; if (routine != NULL) { an_error_code err_code; a_constant_ptr result = local_constant(); folded = fold_gnu_builtin_function_call_if_possible(routine, args, call, result, &err_code); if (err_code != ec_no_error) { check_assertion(!folded); expr_pos_error(err_code, &op->position); conv_to_error_operand(op); } else if (folded) { /* Replace the call with a constant result. */ an_operand orig_op; copy_operand(op, &orig_op); if (expr_stack->statement_expression_seen) { result->folded_statement_expression = TRUE; } /* if */ make_constant_operand(result, op); restore_operand_details(op, &orig_op); if (curr_expr_kind_is_one_in_which_const_exprs_are_recorded() && !constant_is(result, ck_template_param)) { /* Record the call as a backing expression, but not when the call was put into a template parameter constant result. */ op->variant.constant.expr = call; } else if (result->folded_statement_expression) { /* If we are not recording the backing expression, but the call contained a GNU statement expression, be sure to also drop auxiliary structures for the associated statements (pragmas and source sequence entries). */ eliminate_statement_expr_pragmas(call); #if GENERATE_SOURCE_SEQUENCE_LISTS eliminate_statement_expr_src_seq_entries(call); #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ } /* if */ } /* if */ release_local_constant(&result); } /* if */ } /* if */ return folded; } /* fold_gnu_call_if_possible */ static void scan_and_process_builtin_launder_arg( an_operand *func_op, a_rescan_control_block *rcblock, an_operand *result) /* Parse the argument in a construct of the form __builtin_launder( ) The left parenthesis is already consumed. must be a pointer and its type is imbued on the pseudo-call (without a cast). The scanned construct is recorded in *result. If this construct is being substituted ("rescanned") rcblock provides the associated rescan information. */ { a_boolean err = FALSE; an_operand arg; an_expr_node_ptr node, callee; a_type_ptr call_type; a_transformation_options_set callee_transform_options = TOPT_WILL_CALL | TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION | TOPT_SUPPRESS_CHECK_FOR_INDEFINITE_FUNCTION; do_operand_transformations(func_op, callee_transform_options); if (rcblock == NULL) { /* Scan the argument operand. */ scan_expr(&arg, PREC_LOWEST, EOPT_DISALLOW_COMMA_OPERATOR); } else { /* Convert the previously-scanned expression to an_operand form. */ make_rescan_operand(rcblock->argument_list, rcblock, &arg); } /* if */ do_operand_transformations(&arg, TOPT_NO_OPTIONS); if (is_error_operand(&arg)) { normalize_error_operand(&arg); err = TRUE; } else if (!is_plain_pointer_type(arg.type) && !is_template_param_type(arg.type)) { type_error_in_operand(ec_type_not_pointer, &arg, arg.type); err = TRUE; } /* if */ if (err) { make_error_operand(result); } else { callee = make_node_from_operand(func_op); callee->next = make_node_from_operand(&arg); call_type = make_routine_type(arg.type, arg.type, /*param2_type=*/NULL, /*param3_type=*/NULL, /*param4_type=*/NULL); make_function_call(callee, call_type, /*is_virtual=*/FALSE, /*virtual_suppressed=*/FALSE, /*selector_is_object_pointer=*/FALSE, /*compiler_generated=*/FALSE, /*is_conversion=*/FALSE, /*arg_dep_lookup_suppressed=*/FALSE, /*qualified_function_name=*/FALSE, /*found_through_adl=*/FALSE, /*uses_operator_syntax=*/FALSE, /*start_position=*/&null_source_position, /*operator_position=*/&func_op->position, /*end_position=*/&null_source_position, result, /*p_folded=*/(a_boolean*)NULL, &node); } /* if */ } /* scan_and_process_builtin_launder_arg */ static void scan_expr_for_builtin_choose_expr(an_expr_node *node, an_operand *result, a_boolean *err) /* Scan the second or third operand (including the leading comma) of a GNU C __builtin_choose_expr construct. node points to the enk_builtin_choose_expr node for the construct and is updated as needed with the results of the scan. *err is set to TRUE if errors are detected; if *err is already set to TRUE, some diagnostics are inhibited. *result is the operand representing the __builtin_choose_expr operation; this routine may turn it into a constant operand (with node as the backing expression) if needed. */ { an_expr_node_ptr *p_arg; a_boolean is_evaluated; check_assertion(node->kind == (an_expr_node_kind)enk_builtin_choose_expr); p_arg = &node->variant.builtin_choose_expr.operands->next; if (*p_arg == NULL) { /* We're scanning the second operand. */ is_evaluated = node->variant.builtin_choose_expr.choose_first; } else { /* We're scanning the third operand. */ is_evaluated = !node->variant.builtin_choose_expr.choose_first; p_arg = &(*p_arg)->next; } /* if */ if (curr_token == tok_comma) { an_operand operand; a_boolean saved_evaluated = expr_stack->evaluated; expr_stack->evaluated = is_evaluated; /* Skip the comma. */ (void)get_token(); scan_expr(&operand, PREC_LOWEST, EOPT_DISALLOW_COMMA_OPERATOR); if (is_evaluated) { if (curr_expr_kind_is_const()) { do_operand_transformations(&operand, TOPT_NO_OPTIONS); force_operand_to_constant_if_possible(&operand); if (!is_constant_operand(&operand)) { if (!is_error_operand(&operand)) { error_in_operand(ec_expr_not_constant, &operand); } /* if */ } else { /* Make sure the overall operand is a constant also. Use the expression representation as a backing expression. */ copy_operand(&operand, result); result->variant.constant.expr = node; } /* if */ } /* if */ node->type = operand.type; node->is_lvalue = is_an_lvalue(&operand) || is_a_function_designator(&operand); result->type = operand.type; result->state = operand.state; } /* if */ expr_stack->evaluated = saved_evaluated; *p_arg = make_node_from_operand(&operand); } else { if (!*err) { expr_pos_error(ec_exp_comma, &pos_curr_token); } /* if */ flush_tokens(); *err = TRUE; *p_arg = error_node(); } /* if */ } /* scan_expr_for_builtin_choose_expr */ static void scan_and_process_builtin_choose_expr_args(an_operand *result) /* Parse the three comma-separated arguments in a construct of the form __builtin_choose_expr( , , ) The left parenthesis is already consumed. must be a constant-expression and determines whether (if is true) or (if is false) is selected for evaluation. The scanned construct is recorded in *result; if the selected expression produces a constant, *result will be a constant operand with the full construct recorded as a backing expression. Only available in C mode. */ { a_boolean err = FALSE; an_operand selector_op; an_expr_stack_entry expr_stack_entry; an_expr_node_ptr node; check_assertion(C_mode()); node = alloc_expr_node((an_expr_node_kind)enk_builtin_choose_expr); make_expression_operand(node, result); /* Scan the selector expression, which must be a scalar constant. */ push_expr_stack((an_expression_kind)ek_init_constant, &expr_stack_entry, /*force_object_lifetime=*/FALSE, /*suppress_object_lifetime=*/FALSE); scan_expr(&selector_op, PREC_LOWEST, EOPT_DISALLOW_COMMA_OPERATOR); do_operand_transformations(&selector_op, TOPT_NO_OPTIONS); pop_expr_stack(); if (is_error_operand(&selector_op)) { normalize_error_operand(&selector_op); err = TRUE; } else if (!is_scalar_type(selector_op.type)) { error_in_operand(ec_expr_not_scalar, &selector_op); err = TRUE; } else { if (is_expression_operand(&selector_op)) { (void)expr_interpret_expression_operand(&selector_op, /*must_be_constant=*/TRUE, /*is_constant_evaluated=*/TRUE); } /* if */ if (!op_is_false_constant(&selector_op)) { node->variant.builtin_choose_expr.choose_first = TRUE; } /* if */ } /* if */ node->variant.builtin_choose_expr.operands = make_node_from_operand(&selector_op); /* Now scan the second and third argument. */ scan_expr_for_builtin_choose_expr(node, result, &err); scan_expr_for_builtin_choose_expr(node, result, &err); if (err) { make_error_operand(result); } else { force_operand_to_constant_if_possible(result); } /* if */ } /* scan_and_process_builtin_choose_expr_args */ static void scan_builtin_pseudo_call(an_operand *operand, a_rescan_control_block *rcblock, an_operand *result_op) /* Operand represents a built-in function that needs special treatment when called (e.g., the arguments cannot be evaluated). This function parses and -- when appropriate -- evaluates a pseudo-call to the built-in function. *result_op is set to an operand representing the entire pseudo-call. If rcblock is non-NULL, we are redoing semantic analysis on a previously-scanned call; rcblock->expr points to the previously-scanned call, and rcblock->argument_list to the previously-scanned argument list. */ { an_operand arg; an_expr_stack_entry expr_stack_entry; a_routine_ptr rp = routine_from_function_operand(operand); a_builtin_function_kind bfk; a_type_ptr result_type; a_constant_ptr result = local_constant(); a_source_position lparen_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ an_operand dummy_bound_function_selector; a_boolean regular_case = FALSE; check_assertion(rp != NULL && is_gnu_builtin_function(rp)); bfk = rp->variant.builtin_function_kind; if (rcblock == NULL) { /* Pick up the "(" and add ")" as a stop token. */ check_assertion(curr_token == tok_lparen); lparen_position = pos_curr_token; (void)get_token(); add_matching_stop_token(tok_rparen); } /* if */ switch (bfk) { case bufk_launder: scan_and_process_builtin_launder_arg(operand, rcblock, result_op); break; case bufk_choose_expr: check_assertion(C_mode()); /* rcblock is not passed down. */ scan_and_process_builtin_choose_expr_args(result_op); break; #if GCC_BUILTIN_VARARGS case bfk_stdarg_start: case bfk_va_start: scan_va_start_operator(result_op, operand, /*single_operand=*/FALSE); break; case bfk_varargs_start: scan_va_start_operator(result_op, operand, /*single_operand=*/TRUE); break; case bfk_va_arg: scan_va_arg_operator(result_op, operand); break; case bfk_va_end: scan_va_end_operator(result_op, operand); break; case bfk_va_copy: scan_va_copy_operator(result_op, operand); break; #endif /* GCC_BUILTIN_VARARGS */ default: regular_case = TRUE; } /* switch */ if (regular_case) { /* Simple cases involving just one unevaluated argument (i.e., the argument is parsed "as if" for a sizeof operator). The expression stack cannot be popped until after the argument has been transformed; so we record whether we are in a constant-expression prior to updating the stack. */ a_boolean saved_favor_constant_result; a_boolean in_constant_expression = curr_expr_kind_is_const(); an_expression_kind ek = (an_expression_kind)ek_sizeof; if (bfk == bfk_constant_p) { if (!always_fold_calls_to_builtin_constant_p && !in_constant_expression && innermost_function_scope != NULL) { ek = ek_normal; } /* if */ if (clang_version_is(>= 200000)) { /* Clang 20 has a bizarre behavior that forces aggressive folding not only for the folding of the operand of __builtin_constant_p, but also for subsequent parts of the expression. For example, it accepts the following even though it involves a reinterpret_cast: int x = 42, y = 42; static_assert(__builtin_constant_p(x) ? (char*)&x != (char*)&y : (char*)&x != (char*)&y); By setting a few flags in the expression stack, we ensure that the casts are folded immediately rather than in the interpreter. */ expr_stack->favor_constant_result = TRUE; expr_stack->consteval_call_need_not_fold = FALSE; } /* if */ } /* if */ push_expr_stack_with_rcblock(ek, &expr_stack_entry, /*force_object_lifetime=*/FALSE, /*suppress_object_lifetime=*/FALSE, rcblock); saved_favor_constant_result = expr_stack->favor_constant_result; if (bfk == bfk_constant_p) { expr_stack->favor_constant_result = TRUE; expr_stack->consteval_call_need_not_fold = FALSE; if (!always_fold_calls_to_builtin_constant_p && !in_constant_expression && innermost_function_scope != NULL) { /* Inside function scopes, __builtin_constant_p is only folded if its argument is a constant-expression. In such cases, the argument must therefore be treated as "potentially evaluated". */ expr_stack->potentially_evaluated = TRUE; } /* if */ expr_stack->unevaluated_expr_will_be_kept_in_il = TRUE; } /* if */ if (rcblock == NULL) { /* Parse the pseudo-call argument. GNU compilers accept multiple arguments and no argument, but that does not seem a useful thing to emulate. So we'll issue a syntax error in those cases. */ scan_expr(&arg, PREC_LOWEST, EOPT_DISALLOW_COMMA_OPERATOR); } else { /* Convert the previously-scanned expression to an_operand form. */ make_rescan_operand(rcblock->argument_list, rcblock, &arg); } /* if */ eliminate_unusual_operand_kinds(&arg); if (bfk == (a_builtin_function_kind)bfk_constant_p && gcc_const_variables_allowed && innermost_function_scope != NULL && (gcc_mode || (gpp_mode && (gnu_version >= 40300 || (gnu_version >= 40000 && gnu_version < 40100)))) && is_expression_operand(&arg)) { /* Whether GCC produces a true value for __builtin_constant_p(x) if x is an initialized const variable varies on a number of conditions, including the optimization level requested from GCC. We cannot emulate that condition exactly, but the condition above comes close if gcc_const_variables_allowed is interpreted as "optimization level -O1 or above". In order for the front end to be able to fold the constant value of the variable, an lvalue-to-rvalue conversion must take place. This should not be done for other pseudo-functions because it can affect their result (e.g., __builtin_classify_type). */ an_expr_node_ptr arg_expr = expr_node_from_operand(&arg); arg_expr = skip_parens(arg_expr); if (is_variable_node(arg_expr)) { a_variable_ptr vp = node_variable(arg_expr); if (!vp->source_corresp.is_local_to_function && is_potentially_constant_valued_variable(vp) && var_constant_value_full(vp, /*copy_for_reuse=*/FALSE, /*clear_backing_expr=*/FALSE, /*allow_C_mode_const_var=*/TRUE) != NULL) { do_operand_transformations(&arg, TOPT_NO_OPTIONS); } /* if */ } /* if */ } /* if */ if (is_expression_operand(&arg) && is_a_prvalue(&arg)) { (void)expr_interpret_expression_operand(&arg, /*must_be_constant=*/FALSE, /*is_constant_evaluated=*/TRUE); } /* if */ expr_stack->favor_constant_result = saved_favor_constant_result; /* Now determine the constant result of the pseudo-call by examining the (unevaluated) argument expression. */ result_type = return_type_of(rp->type); result_type = skip_typerefs(result_type); check_assertion(is_integral_type(result_type)); if (is_template_dependent_context() && (is_indefinite_function_operand(operand) || is_template_param_type(arg.type) || (is_constant_operand(&arg) && arg.variant.constant.kind == (a_constant_repr_kind)ck_template_param))) { /* The argument is template-dependent, so we can't fold. Generate a call. */ prep_generic_operand(&arg); do_operand_transformations(operand, TOPT_NO_OPTIONS); change_some_ref_kinds(operand->ref_entries_list, SRK_ADDRESS_TAKEN, SRK_REFERENCE); if (is_error_operand(&arg)) { make_error_operand(result_op); } else { #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(operand, &dummy_bound_function_selector, make_node_from_operand(&arg), /*compiler_generated=*/FALSE, /*arg_dep_lookup_suppressed=*/FALSE, /*qualified_function_name=*/FALSE, /*found_through_adl=*/FALSE, /*uses_operator_syntax=*/FALSE, /*start_position=*/&null_source_position, /*operator_position=*/&operand->position, /*end_position=*/&null_source_position, result_op, /*p_folded=*/(a_boolean*)NULL, (an_expr_node_ptr *)NULL); if (is_constant_operand(&arg) || in_constant_expression || bfk == (a_builtin_function_kind)bfk_classify_type) { make_template_param_expr_constant_operand(result_op); } /* if */ } /* if */ goto result_built; } /* if */ switch (bfk) { case bfk_constant_p: /* Except for string literals, GNU compilers do not treat address constants as constants. The GNU compiler front ends only fold the call when the argument is a constant-expression, or when the calls appears in a namespace scope initializer; otherwise, the call is folded in the back end, and the result may depend on the optimization level. DEFAULT_ALWAYS_FOLD_CALLS_TO_BUILTIN_CONSTANT_P determines whether we always fold the call in the front end by default (setting it to FALSE matches the GNU compiler more closely, but requires support in the back end). There was a brief period around gcc 3.4.0 when gcc did not fold cases with result_value equal to 0 when we are in a constant expression but none of the other conditions apply, but we've decided that's an aberration. */ { a_boolean result_value, fold; result_value = operand_is_string_literal(&arg) || (is_constant_operand(&arg) && !constant_is(&arg.variant.constant, ck_address)); fold = result_value || innermost_function_scope == NULL || (always_fold_calls_to_builtin_constant_p && !in_potential_constant_constexpr_context()) || in_constant_expression; if (!fold || curr_expr_kind_is_one_in_which_const_exprs_are_recorded()) { /* Represent an actual call in the IL. (The usual transformations, including promotion, are needed.) */ do_operand_transformations(operand, TOPT_NO_OPTIONS); change_some_ref_kinds(operand->ref_entries_list, SRK_ADDRESS_TAKEN, SRK_REFERENCE); arg_default_promote_operand(&arg, /*is_ellipsis=*/TRUE); #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(operand, &dummy_bound_function_selector, make_node_from_operand(&arg), /*compiler_generated=*/FALSE, /*arg_dep_lookup_suppressed=*/FALSE, /*qualified_function_name=*/FALSE, /*found_through_adl=*/FALSE, /*uses_operator_syntax=*/FALSE, /*start_position=*/&null_source_position, /*operator_position=*/&operand->position, /*end_position=*/&null_source_position, result_op, /*p_folded=*/(a_boolean*)NULL, (an_expr_node_ptr *)NULL); } /* if */ if (fold) { an_expr_node *backing_expr = NULL; if (curr_expr_kind_is_one_in_which_const_exprs_are_recorded()) { backing_expr = make_node_from_operand(result_op); } /* if */ set_integer_constant(result, (a_host_large_integer)result_value, result_type->variant.integer.int_kind); make_constant_operand(result, result_op); result_op->variant.constant.expr = backing_expr; } /* if */ } break; case bfk_classify_type: #if FIXED_POINT_ALLOWED if (fixed_point_enabled && is_fixed_point_type(arg.type)) { expr_pos_error(ec_no_classification_for_fixed_point_type, &arg.position); } /* if */ #endif /* FIXED_POINT_ALLOWED */ set_integer_constant(result, (a_host_large_integer) gnu_type_class_for_type(arg.type), result_type->variant.integer.int_kind); make_constant_operand(result, result_op); break; default: unexpected_condition(); } /* switch */ result_built: pop_expr_stack(); } /* if */ if (rcblock == NULL) { /* Scan the closing ")". */ #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ (void)required_token(tok_rparen, ec_exp_rparen); remove_matching_stop_token(tok_rparen); } else { #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = rcblock->expr->expr_range.end; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ lparen_position = rcblock->expr->position; } /* if */ set_operand_position(result_op, &operand->position, &end_position, &lparen_position); check_assertion(is_constant_operand(result_op) || is_error_operand(result_op) || !curr_expr_kind_is_const()); release_local_constant(&result); } /* scan_builtin_pseudo_call */ /* Forward declarations. */ struct a_builtin_call_adjustment; /* Define a callback type for adjusting builtins. */ typedef a_routine_ptr a_builtin_call_adjustment_callback( an_operand *target, an_arg_list_elem_ptr args, a_source_position *closing_paren_position, a_builtin_call_adjustment *bcap, an_expr_node_ptr *arg_list); /* The following are function declarations (despite looking like variables). */ static a_builtin_call_adjustment_callback adjust_sync_atomic_builtin, adjust_builtin_zero_non_value_bits, adjust_overflow_builtin, adjust_elementwise_or_reduce_builtin, adjust_type_generic_builtin, adjust_return_type_to_type_of_first_argument, adjust_masked_builtin, adjust_srcloc_builtin; /* Structure used to pass information from builtin_call_needs_adjustment to its caller or the callback to describe a particular builtin call. The fields are set based on builtin_function_kind and adjustments to the parameter types or concrete routine to be called are made as needed based on their values. */ typedef struct a_builtin_call_adjustment { a_builtin_call_adjustment_callback *callback; /* When not nullptr, a pointer to a callback routine to be invoked after the arguments of the builtin call have been scanned. */ a_type_ptr result_type; /* If non-NULL, the return type of the builtin. If the return type of the routine is dependent on its arguments, this field will be NULL. */ a_symbol_ptr overloaded_function_symbol; /* If non-NULL, the symbol for operator new/delete to be used in place of __builtin_operator_new/ builtin_operator_delete. */ a_name_reference_ptr name_reference; /* If non-NULL, the special name reference to use to represent __builtin_operator_new/ __builtin_operator_delete to a back end. */ int n_args; /* The number of arguments the builtin expects. */ int dispatch_arg; /* The argument number of the "dispatch type", if any. Most of the adjustments involve looking at the type of the argument passed in this position to determine the return type and/or types of other parameters. For example, __sync_fetch_and_add(&x, 3, ignored()) must be replaced by (typeof(x))__sync_fetch_and_add_4((void*)&x, (typeof(x)3)) In this case, dispatch_arg == 1 (and the return type and types of the first two parameters are checked based upon its type). Almost always set to 1. */ a_boolean replace_routine_type; /* When TRUE, a new routine type (and hence new routine) will be generated for this call, based on the "dispatch type". Note that this results in routines with the same name but different types, e.g.: int f(_Atomic(char) *pac, char c, _Atomic(int) *pai, int i) { return __c11_atomic_load(pac, c) + __c11_atomic_load(pai, i); } These two invocations result in different routines (one that returns a "char" and a second that returns an "int"). */ a_boolean is_c11_atomic; /* TRUE if the builtin is a Clang __c11_atomic_* builtin. The type of these builtins depends on the "dispatch type" but these are not dispatched to pre-defined concrete routines, rather a new routine (with its own type) is created for each invocation. When this flag is TRUE, replace_routine_type will also be TRUE. */ a_boolean is_sync; /* TRUE if the builtin is of the __sync_* variety. */ a_boolean is_atomic; /* TRUE if the builtin is of the __atomic_* variety (but not __c11_atomic_*). */ a_boolean is_generic; /* TRUE if this is a generic GCC atomic function (i.e., atomic_load/store/compare/compare_exchange). In this case, the generic routine is replaced by a call to a routine of the same name with a _1, _2, _4, _8, or _16 suffix as determined by the type pointed to by the first argument. */ a_boolean is_elementwise; /* TRUE if this is a clang __builtin_elementwise_* builtin. */ a_boolean is_reduce; /* TRUE if this is a clang __builtin_reduce_* builtin. */ a_boolean is_invoke; /* TRUE if this is a clang __builtin_invoke builtin. */ a_boolean has_trailing_n; /* TRUE if this is a GCC atomic function with a trailing "_n" in its name (which is replaced by the _1, _2, _4, _8, or _16 suffix). */ } a_builtin_call_adjustment; static a_boolean builtin_call_needs_adjustment(a_routine_ptr rout, a_builtin_call_adjustment *bcap) /* A builtin function (described by rout) is being called. See if it is a builtin function that requires special processing; if so, set the appropriate flags in *bcap and return TRUE. The vast majority of builtin functions require no special treatment, but certain sets of builtin functions (e.g, __sync__..., __atomic_..., __c11_atomic...) require various modifications. After the arguments are scanned, the routine pointed to by bcap->callback will be called to check and adjust the argument and routine types as needed. */ { a_boolean requires_processing = TRUE; a_builtin_function_kind bfk; /* Initialize all fields in *bcap (note that fields are initialized to their most likely value, not necessarily zero or NULL). */ bcap->callback = adjust_sync_atomic_builtin; /* Assume most likely case. */ bcap->result_type = NULL; bcap->overloaded_function_symbol = NULL; bcap->name_reference = NULL; bcap->n_args = 0; bcap->dispatch_arg = 1; bcap->replace_routine_type = FALSE; bcap->is_c11_atomic = FALSE; bcap->is_sync = FALSE; bcap->is_atomic = FALSE; bcap->is_generic = FALSE; bcap->is_elementwise = FALSE; bcap->is_reduce = FALSE; bcap->is_invoke = FALSE; bcap->has_trailing_n = FALSE; bfk = rout->variant.builtin_function_kind; /* Set fields of *bcap as determined by the particular builtin. */ switch (bfk) { case bfk_sync_lock_release: bcap->result_type = void_type(); bcap->n_args = 1; bcap->is_sync = TRUE; break; case bfk_sync_fetch_and_add: case bfk_sync_fetch_and_sub: case bfk_sync_fetch_and_or: case bfk_sync_fetch_and_and: case bfk_sync_fetch_and_xor: case bfk_sync_fetch_and_nand: case bfk_sync_add_and_fetch: case bfk_sync_sub_and_fetch: case bfk_sync_or_and_fetch: case bfk_sync_and_and_fetch: case bfk_sync_xor_and_fetch: case bfk_sync_nand_and_fetch: case bfk_sync_lock_test_and_set: case bfk_sync_swap: bcap->n_args = 2; bcap->is_sync = TRUE; break; case bfk_sync_bool_compare_and_swap: bcap->result_type = bool_type(); FALLTHROUGH case bfk_sync_val_compare_and_swap: bcap->n_args = 3; bcap->is_sync = TRUE; break; case bfk_atomic_load_n: bcap->n_args = 2; bcap->has_trailing_n = TRUE; bcap->is_atomic = TRUE; break; case bfk_atomic_load: case bfk_atomic_store: bcap->result_type = void_type(); bcap->n_args = 3; bcap->is_atomic = TRUE; bcap->is_generic = TRUE; break; case bfk_atomic_store_n: bcap->result_type = void_type(); bcap->n_args = 3; bcap->is_atomic = TRUE; bcap->has_trailing_n = TRUE; break; case bfk_atomic_exchange_n: bcap->n_args = 3; bcap->is_atomic = TRUE; bcap->has_trailing_n = TRUE; break; case bfk_atomic_exchange: bcap->result_type = void_type(); bcap->n_args = 4; bcap->is_atomic = TRUE; bcap->is_generic = TRUE; break; case bfk_atomic_compare_exchange: bcap->result_type = bool_type(); bcap->n_args = 6; bcap->is_atomic = TRUE; bcap->is_generic = TRUE; break; case bfk_atomic_compare_exchange_n: bcap->result_type = bool_type(); bcap->n_args = 6; bcap->has_trailing_n = TRUE; bcap->is_atomic = TRUE; break; case bfk_atomic_add_fetch: case bfk_atomic_fetch_add: case bfk_atomic_sub_fetch: case bfk_atomic_fetch_sub: case bfk_atomic_and_fetch: case bfk_atomic_fetch_and: case bfk_atomic_xor_fetch: case bfk_atomic_fetch_xor: case bfk_atomic_or_fetch: case bfk_atomic_fetch_or: case bfk_atomic_nand_fetch: case bfk_atomic_fetch_nand: bcap->n_args = 3; bcap->is_atomic = TRUE; break; case bfk___c11_atomic_init: bcap->is_c11_atomic = TRUE; bcap->result_type = void_type(); bcap->n_args = 2; break; case bfk___c11_atomic_load: bcap->is_c11_atomic = TRUE; bcap->n_args = 2; break; case bfk___c11_atomic_store: bcap->is_c11_atomic = TRUE; bcap->result_type = void_type(); bcap->n_args = 3; break; case bfk___c11_atomic_exchange: bcap->is_c11_atomic = TRUE; bcap->n_args = 3; break; case bfk___c11_atomic_compare_exchange_strong: case bfk___c11_atomic_compare_exchange_weak: bcap->is_c11_atomic = TRUE; bcap->result_type = bool_type(); bcap->n_args = 5; break; case bfk___c11_atomic_fetch_add: case bfk___c11_atomic_fetch_and: case bfk___c11_atomic_fetch_or: case bfk___c11_atomic_fetch_sub: case bfk___c11_atomic_fetch_xor: bcap->is_c11_atomic = TRUE; bcap->n_args = 3; break; case bfk_nontemporal_load: case bfk_arm_ldrex: case bfk_arm_ldaex: case bfk_arm_ldg: bcap->replace_routine_type = TRUE; bcap->n_args = 1; break; case bfk_nontemporal_store: bcap->replace_routine_type = TRUE; bcap->result_type = void_type(); bcap->n_args = 2; bcap->dispatch_arg = 2; break; case bfk_arm_addg: case bfk_arm_irg: bcap->replace_routine_type = TRUE; bcap->n_args = 2; break; case bfk_operator_delete: case bfk_operator_new: /* Replace the reference to __builtin_operator_delete/ __builtin_operator_new with a reference to the appropriate global operator. The caller will perform overload resolution to select the proper variant of the function. */ bcap->overloaded_function_symbol = opname_function_symbol( bfk == (a_builtin_function_kind)bfk_operator_new ? (an_opname_kind)onk_new : (an_opname_kind)onk_delete); /* Allocate a name reference to indicate to a back end that this transformation has occurred (so the original source can be recreated by a back end). */ bcap->name_reference = alloc_name_reference(); bcap->name_reference->special_kind = (bfk == (a_builtin_function_kind)bfk_operator_new) ? (a_special_function_kind)sfk_builtin_operator_new : (a_special_function_kind)sfk_builtin_operator_delete; bcap->callback = nullptr; break; case bfk_zero_non_value_bits: /* Callback will validate arguments. */ bcap->callback = adjust_builtin_zero_non_value_bits; break; case bfk_add_overflow: case bfk_add_overflow_p: case bfk_sub_overflow: case bfk_sub_overflow_p: case bfk_mul_overflow: case bfk_mul_overflow_p: bcap->callback = adjust_overflow_builtin; break; case bfk_source_location: { /* The source location builtin has been used, require and process the GNU std::source_location::__impl type here. Note that this result is only used in clang mode, however, this call is unconditional as it runs required checks for the type in the current translation unit. */ a_type_ptr result_type = gnu_source_location_impl_type(); /* GCC and (for better compatibility with libstdc++) EDG declare the builtin function's return type as void*. Although the type of the builtin is adjusted in clang mode (see below), this occurs only upon first use; thus, we initially assume no processing is required). */ requires_processing = FALSE; if (clang_mode) { a_type_ptr curr_result_ty = rout->type->variant.routine.return_type; /* Wrap the result_type with const qualification. */ result_type = make_qualified_type(result_type, TQ_CONST); /* Clang uses a pointer to the const __impl type as the return type; if the result type has not been updated, do that now. */ if (!is_pointer_type_to_type(curr_result_ty, result_type)) { bcap->result_type = make_pointer_type(result_type); bcap->callback = adjust_srcloc_builtin; requires_processing = TRUE; } /* if */ } /* if */ } break; case bfk_elementwise_abs: case bfk_elementwise_acos: case bfk_elementwise_asin: case bfk_elementwise_atan: case bfk_elementwise_bitreverse: case bfk_elementwise_canonicalize: case bfk_elementwise_ceil: case bfk_elementwise_cos: case bfk_elementwise_cosh: case bfk_elementwise_exp: case bfk_elementwise_exp2: case bfk_elementwise_floor: case bfk_elementwise_log: case bfk_elementwise_log10: case bfk_elementwise_log2: case bfk_elementwise_nearbyint: case bfk_elementwise_popcount: case bfk_elementwise_rint: case bfk_elementwise_round: case bfk_elementwise_roundeven: case bfk_elementwise_sin: case bfk_elementwise_sinh: case bfk_elementwise_sqrt: case bfk_elementwise_tan: case bfk_elementwise_tanh: case bfk_elementwise_trunc: bcap->n_args = 1; bcap->is_elementwise = TRUE; bcap->callback = adjust_elementwise_or_reduce_builtin; break; case bfk_elementwise_atan2: case bfk_elementwise_copysign: case bfk_elementwise_fmod: case bfk_elementwise_ldexp: case bfk_elementwise_max: case bfk_elementwise_maximum: case bfk_elementwise_min: case bfk_elementwise_minimum: case bfk_elementwise_add_sat: case bfk_elementwise_sub_sat: case bfk_elementwise_pow: case bfk_elementwise_clzg: case bfk_elementwise_ctzg: bcap->n_args = 2; bcap->is_elementwise = TRUE; bcap->callback = adjust_elementwise_or_reduce_builtin; break; case bfk_elementwise_fma: case bfk_elementwise_fshl: case bfk_elementwise_fshr: bcap->n_args = 3; bcap->is_elementwise = TRUE; bcap->callback = adjust_elementwise_or_reduce_builtin; break; case bfk_reduce_and: case bfk_reduce_max: case bfk_reduce_maximum: case bfk_reduce_min: case bfk_reduce_minimum: case bfk_reduce_or: case bfk_reduce_xor: case bfk_reduce_add: case bfk_reduce_mul: bcap->n_args = 1; bcap->is_reduce = TRUE; bcap->callback = adjust_elementwise_or_reduce_builtin; break; case bfk_clzg: case bfk_ctzg: bcap->n_args = 2; bcap->callback = adjust_type_generic_builtin; break; case bfk_ffsg: case bfk_parityg: case bfk_popcountg: bcap->n_args = 1; bcap->callback = adjust_type_generic_builtin; break; case bfk_preserve_access_index: bcap->n_args = 1; bcap->replace_routine_type = TRUE; bcap->callback = adjust_return_type_to_type_of_first_argument; break; case bfk_nondeterministic_value: bcap->n_args = 1; bcap->replace_routine_type = TRUE; bcap->callback = adjust_return_type_to_type_of_first_argument; break; case bfk_invoke: bcap->is_invoke = TRUE; bcap->callback = nullptr; break; case bfk_masked_expand_load: case bfk_masked_load: bcap->n_args = 2; bcap->replace_routine_type = TRUE; bcap->callback = adjust_masked_builtin; break; case bfk_masked_gather: bcap->n_args = 3; bcap->replace_routine_type = TRUE; bcap->callback = adjust_masked_builtin; break; default: /* No special processing is needed for most builtins. */ requires_processing = FALSE; break; } /* switch */ if (bcap->is_c11_atomic) { bcap->replace_routine_type = TRUE; } /* if */ return requires_processing; } /* builtin_call_needs_adjustment */ static a_symbol_ptr gnu_builtin_func_by_name(a_const_char *name) /* Return the symbol for the GNU __builtin_... function identified by the given string, or NULL if there is no such function. */ { a_symbol_locator loc; a_symbol_ptr sym; clear_locator(&loc, &null_source_position); (void)find_symbol(name, (sizeof_t)strlen(name), &loc); sym = file_scope_id_lookup(il_header.primary_scope, &loc, IDL_DIRECT_NAMESPACE_MEMBERS_ONLY | IDL_SUPPRESS_DECL_SEQ_CHECK); if (builtin_needs_to_be_loaded(loc.symbol_header)) { /* Load the builtin function and redo the lookup. */ (void)load_matching_builtin_function(loc.symbol_header); sym = file_scope_id_lookup(il_header.primary_scope, &loc, IDL_DIRECT_NAMESPACE_MEMBERS_ONLY | IDL_SUPPRESS_DECL_SEQ_CHECK); } /* if */ /* Cover the unlikely case that the symbol name corresponds to multiple entries. */ /*lint --e{850} sym modified in loop */ for (; sym != NULL; sym = sym->next) { if (symbol_is(sym, sk_overloaded_function)) { sym = sym->variant.overloaded_function.symbols; } else if (symbol_is(sym, sk_routine) && is_gnu_builtin_function(sym->variant.routine.ptr)) { break; } /* if */ } /* for */ return sym; } /* gnu_builtin_func_by_name */ static a_symbol_ptr builtin_with_particular_type(a_routine_ptr rout, a_type_ptr new_rout_type) /* Given a builtin routine, return the symbol (created if needed) of a builtin with the same name but the specified type. This is needed because some builtins can take many different types, e.g., T __builtin_elementwise_max(T x, T y) can be called with integer, floating-point or vector types and the front end generates a concrete routine for each separate type. */ { a_symbol_ptr sym; a_symbol_locator loc; /* See if a version of this routine with the specified type has already been created; if so, reuse that routine. */ clear_locator(&loc, &null_source_position); (void)find_symbol(rout->source_corresp.name, strlen(unmangled_or_fabricated_name_of( &rout->source_corresp)), &loc); for (sym = loc.symbol_header->symbol; sym != NULL; sym = sym->next) { if (sym->kind == (a_symbol_kind)sk_routine && sym->is_invisible && identical_types(new_rout_type, sym->variant.routine.ptr->type)) { break; } /* if */ } /* for */ if (sym == NULL) { /* Create a new routine with the type as determined above and the same name for the concrete routine to use for this call. Back ends will need to deal with multiple routines with the same name and different types. Create an invisible symbol for the new routine (so it won't be found on subsequent lookups). */ a_routine_ptr new_rout, save_next; new_rout = make_routine(new_rout_type, (a_storage_class)sc_extern, DEPTH_OF_FILE_SCOPE); save_next = new_rout->next; *new_rout = *rout; new_rout->type = new_rout_type; new_rout->next = save_next; sym = alloc_symbol((a_symbol_kind)sk_routine, loc.symbol_header, &loc.source_position); sym->variant.routine.ptr = new_rout; sym->is_invisible = TRUE; new_rout->source_corresp.assoc_info = (char*)sym; add_symbol_to_symbol_table(sym, DEPTH_OF_FILE_SCOPE, /*suppress_error=*/TRUE); } /* if */ return sym; } /* builtin_with_particular_type */ static a_routine_ptr adjust_sync_atomic_builtin( an_operand *target, an_arg_list_elem_ptr args, a_source_position *closing_paren_position, a_builtin_call_adjustment *bcap, an_expr_node_ptr *arg_list) /* builtin_call_needs_adjustment previously decided that the call being worked on is of a builtin function that requires some special processing (e.g., a __sync_*, __atomic_*, __c11_atomic_* or other builtin). *target describes the function specified in the call; on return it is updated to describe the appropriate concrete function based on the argument types. args gives the argument list. *closing_paren_position gives the position of the final ")" in the argument list. On return, *arg_list is set to point to the argument list in expression form. The concrete routine to call is returned, and the argument list is checked and cast to appropriate types if necessary. bcap->result_type may be further updated if builtin_call_needs_adjustment left it NULL; in either case, it is returned non-NULL to indicate that the final call needs to be cast to the indicated type. */ { a_routine_ptr rout; a_builtin_function_kind bfk; a_source_position first_arg_pos; a_type_ptr dispatch_type, orig_dispatch_type, ptr_type; a_boolean err = FALSE; a_boolean is_sync_or_atomic = bcap->is_sync || bcap->is_atomic; a_boolean skip_arg_prep = FALSE; *arg_list = NULL; rout = routine_from_function_operand(target); check_assertion(rout != NULL); bfk = rout->variant.builtin_function_kind; if (args == NULL) { /* All routines require at least one argument. */ check_assertion(bcap->n_args >= 0); expr_pos_error(ec_bad_type_for_gnu_sync_function, closing_paren_position); conv_to_error_operand(target); err = TRUE; } else { int k = 0; an_arg_list_elem_ptr *arg = &args; first_arg_pos = *init_component_pos(args); /* Truncate the argument list to the length expected by the builtin function. Issue a warning on excess arguments. Issue an error on too few arguments. */ while (k < bcap->n_args && *arg != NULL) { k += 1; arg = p_next_elem(*arg); } /* while */ if (*arg != NULL) { /* *arg points to the first excess argument. */ expr_pos_warning(ec_extra_arguments_ignored, init_component_pos(*arg)); free_arg_list(*arg); *arg = NULL; } else if (k != bcap->n_args) { /* There are too few arguments. */ expr_pos_error(ec_too_few_arguments, closing_paren_position); conv_to_error_operand(target); err = TRUE; goto done; } /* if */ if (bcap->dispatch_arg == 1) { /* In most cases, the "dispatch" type is the first argument. */ ptr_type = operand_of_arg_list_elem(args)->type; } else { /* The second argument is the "dispatch" type. */ check_assertion(bcap->dispatch_arg == 2 && args->next != NULL); ptr_type = operand_of_arg_list_elem(args->next)->type; } /* if */ orig_dispatch_type = ptr_type; dispatch_type = f_skip_typerefs(orig_dispatch_type); /* Do array-to-pointer decay if needed. */ dispatch_type = do_implicit_type_transformations(dispatch_type, (an_operand*)NULL); /* Check that the dispatch type is a pointer. */ if (is_pointer_type(dispatch_type)) { dispatch_type = type_pointed_to(dispatch_type); orig_dispatch_type = dispatch_type; dispatch_type = skip_typerefs(dispatch_type); } else { if (!is_error_type(dispatch_type)) { expr_pos_error(bcap->is_generic ? ec_expr_not_object_pointer : ec_bad_type_for_gnu_sync_function, &first_arg_pos); } /* if */ conv_to_error_operand(target); err = TRUE; goto done; } /* if */ /* Check that the type under the pointer is appropriate. */ a_boolean float_allowed = FALSE; if (is_error_type(dispatch_type)) { /* An error has already been issued. */ expr_expect_error(); err = TRUE; } else if (is_sync_or_atomic && !bcap->is_generic && !is_integral_or_enum_type(dispatch_type) && !is_pointer_type(dispatch_type) && !((float_allowed = (clang_version_is(>=130000) && (bfk == bfk_atomic_add_fetch || bfk == bfk_atomic_sub_fetch || bfk == bfk_atomic_fetch_add || bfk == bfk_atomic_fetch_sub))) && is_floating_type(dispatch_type))) { /* For the __sync_* and __atomic_* builtins, the first argument must be a pointer to an integral type (or enum) or a pointer to a pointer. Certain __atomic_*fetch* operations allow a pointer to "supported" floating-point types in Clang version 13.0.0 and later. It appears that only float and double types are currently accepted, but allow all floating-point types in anticipation of other floating-point types being allowed in different architectures. */ expr_pos_error(float_allowed ? ec_bad_type_for_atomic_fetch : ec_bad_type_for_gnu_sync_function, &first_arg_pos); err = TRUE; } else if (bcap->is_generic) { /* The generic __atomic_... functions take a pointer to a complete object type. */ if (!is_complete_object_type(dispatch_type)) { expr_pos_error(ec_expr_not_object_pointer, &first_arg_pos); err = TRUE; goto done; } /* if */ /* In the "generic" case, we do not have to map "rout" onto a more concrete implementation (but skip argument prep so a back end can see the original types being used). */ skip_arg_prep = TRUE; } else if (is_sync_or_atomic && (dispatch_type->size != 1 && dispatch_type->size != 2 && dispatch_type->size != 4 && dispatch_type->size != 8 #if INT128_EXTENSIONS_ALLOWED && dispatch_type->size != 16 #endif /* INT128_EXTENSIONS_ALLOWED */ )) { expr_pos_error(ec_invalid_gnu_sync_size, &first_arg_pos); err = TRUE; } else { /* Find or create the concrete routine to dispatch the operation to. */ a_symbol_ptr sym; an_operand orig_operand; a_name_reference *nrp = alloc_name_reference(); if (bcap->replace_routine_type) { /* The type of the builtin is dynamic and depends on the "dispatch type". Most of the cases here are currently for the __c11_atomic_* builtins. Based on the dispatch type, create a new routine (with a new type) if one doesn't already exist and use that when validating arguments. */ a_type_ptr rout_type = NULL, pA_type = NULL, C_type = NULL; a_type_ptr M_type = NULL, MO_type = NULL; if (bcap->is_c11_atomic) { if (is_c11_atomic_qualified_type(orig_dispatch_type)) { /* Clang implements builtins for the (_explicit) versions of the C11 Atomic functions (see section 7.17). In that section, there are the following definitions: - An A refers to one of the atomic types. - A C refers to its corresponding non-atomic type. - An M refers to the type of the other argument for arithmetic operations. For atomic integer types, M is C. For atomic pointer types, M is ptrdiff_t. Types that represent these are defined here. */ pA_type = make_pointer_type(orig_dispatch_type); C_type = dispatch_type; M_type = is_integral_type(C_type) ? C_type : integer_type(targ_ptrdiff_t_int_kind); MO_type = integer_type((an_integer_kind)ik_int); } else { /* First argument type is not Atomic-qualified. */ expr_pos_error(ec_must_be_atomic_qualified_type, &first_arg_pos); err = TRUE; goto done; } /* if */ } /* if */ switch (bfk) { case bfk___c11_atomic_init: rout_type = make_routine_type(void_type(), pA_type, C_type, (a_type_ptr)NULL, (a_type_ptr)NULL); break; case bfk___c11_atomic_load: rout_type = make_routine_type(C_type, pA_type, MO_type, (a_type_ptr)NULL, (a_type_ptr)NULL); break; case bfk___c11_atomic_store: rout_type = make_routine_type(void_type(), pA_type, C_type, MO_type, (a_type_ptr)NULL); break; case bfk___c11_atomic_exchange: rout_type = make_routine_type(C_type, pA_type, C_type, MO_type, (a_type_ptr)NULL); break; case bfk___c11_atomic_compare_exchange_strong: case bfk___c11_atomic_compare_exchange_weak: rout_type = make_routine_type(bool_type(), pA_type, make_pointer_type(C_type), C_type, MO_type, MO_type); break; case bfk___c11_atomic_fetch_add: case bfk___c11_atomic_fetch_and: case bfk___c11_atomic_fetch_or: case bfk___c11_atomic_fetch_sub: case bfk___c11_atomic_fetch_xor: rout_type = make_routine_type(C_type, pA_type, M_type, MO_type, (a_type_ptr)NULL); break; case bfk_nontemporal_load: case bfk_arm_ldrex: case bfk_arm_ldaex: rout_type = make_routine_type(orig_dispatch_type, make_pointer_type(orig_dispatch_type), (a_type_ptr)NULL, (a_type_ptr)NULL, (a_type_ptr)NULL); break; case bfk_nontemporal_store: rout_type = make_routine_type(void_type(), dispatch_type, make_pointer_type(dispatch_type), (a_type_ptr)NULL, (a_type_ptr)NULL); break; case bfk_arm_ldg: rout_type = make_routine_type(ptr_type, ptr_type); bcap->result_type = ptr_type; break; case bfk_arm_addg: case bfk_arm_irg: rout_type = make_routine_type(ptr_type, ptr_type, integer_type(ik_unsigned_int)); bcap->result_type = ptr_type; break; default: unexpected_condition(); } /* switch */ /* Create a routine with the desired type. */ sym = builtin_with_particular_type(rout, rout_type); } else { /* For __sync_* and __atomic_* builtins, construct the concrete routine's name by appending the proper suffix and then look for that builtin function. */ Small_string<100> name( unmangled_or_fabricated_name_of(&rout->source_corresp)); if (bcap->has_trailing_n) { /* Remove the trailing "_n" suffix. */ name.truncate_to(name.length() - 2); } /* if */ /* Append _1, _2, _4, _8, or _16. */ name.append("_", dispatch_type->size); /* Look up the resulting concrete routine name: */ sym = gnu_builtin_func_by_name(name.as_temp_characters()); } /* if */ check_assertion(sym != NULL); /* Update the operand: */ orig_operand = *target; make_function_designator_operand(sym, target->is_qualified_name, /*compiler_generated=*/FALSE, &orig_operand.position, end_position_of_operand(&orig_operand), target->ref_entries_list, target); if (is_error_operand(target)) { err = TRUE; } else { check_assertion(is_expression_operand(target) && is_routine_node(target->variant.expression)); target->variant.expression->variant.routine.name_reference = nrp; if (bcap->has_trailing_n) { nrp->special_kind = (a_special_function_kind)sfk_gnu_atomic_nongeneric_function; } else if (is_sync_or_atomic) { nrp->special_kind = (a_special_function_kind)sfk_gnu_sync_concrete_function; } /* if */ conv_function_designator_to_ptr_to_function(target, (a_source_position *)NULL, /*allow_ctor=*/FALSE, /*will_call=*/TRUE); rout = sym->variant.routine.ptr; } /* if */ } /* if */ if (!err) { an_arg_list_elem_ptr ap; a_param_type_ptr ptp = NULL; an_expr_node_ptr end_arg_list = NULL; if (bcap->is_generic) { /* For generic __atomic_... functions, the size of the operand pointed to is passed via an implicitly-inserted first argument. */ an_operand size_operand; a_constant_ptr size_constant = local_constant(); an_expr_node_ptr expr_arg; a_name_reference *nrp = alloc_name_reference();; set_integer_constant(size_constant, (a_host_large_integer)dispatch_type->size, targ_size_t_int_kind); make_constant_operand(size_constant, &size_operand); expr_arg = make_node_from_operand_for_expr_list(&size_operand); *arg_list = expr_arg; end_arg_list = expr_arg; release_local_constant(&size_constant); /* Mark the routine as being a generic __atomic_... function (this is used to strip the added argument in C++- and C-generating back ends). */ target->variant.expression->variant.routine.name_reference = nrp; nrp->special_kind = (a_special_function_kind)sfk_gnu_atomic_generic_function; } /* if */ /* Convert the prescanned arguments to the type expected by the function (if needed) and build the argument list in expression form. */ ptp = skip_typerefs(rout->type)->variant.routine.extra_info ->param_type_list; if (bcap->is_generic) { /* In the case of an atomic generic function, the first parameter is size_t and is unused, so skip it. */ check_assertion(f_identical_types(ptp->type, integer_type(targ_size_t_int_kind), ITF_NO_FLAGS)); ptp = ptp->next; } /* if */ for (ap = args; ap != NULL; ap = next_elem(ap)) { an_expr_node_ptr expr_arg; an_operand *operand; operand = operand_of_arg_list_elem(ap); do_operand_transformations(operand, TOPT_NO_OPTIONS); check_assertion(ptp != NULL); if ((is_pointer_type(operand->type) || is_nullptr_type(operand->type)) && is_integral_type(ptp->type) && (bcap->is_sync || (bcap->is_atomic && !bcap->is_generic && ((bcap->n_args == 6 && ptp->param_num == 3) || (bcap->n_args == 3 && ptp->param_num == 2))))) { /* Pointer operands for the "value" parameters of __sync_... and __atomic_... functions must be converted to an integer value. */ cast_operand(ptp->type, operand, /*is_implicit_cast=*/TRUE); } else if (bcap->is_generic && ptp->param_num > 1 && is_pointer_type(ptp->type)) { /* For generic __atomic_... functions, check that the types pointed to by subsequent pointer operands match the type pointed to by the first argument. */ /* A pointer to a complete object type is required. */ a_type_ptr arg_type = skip_typerefs(operand->type); if (!is_pointer_to_object_type(arg_type)) { expr_pos_error(ec_expr_not_object_pointer, &operand->position); goto done; } else { arg_type = type_pointed_to(arg_type); if (is_incomplete_type(arg_type)) { expr_pos_error(ec_expr_not_object_pointer, &operand->position); goto done; } else if (skip_typerefs(arg_type)->size != dispatch_type->size) { /* The type pointed to by this argument has a different size than the type pointed to by the first argument. */ if (expr_error_should_be_issued()) { pos_opt_ty2_error(ec_incompatible_param, &operand->position, ptr_type, operand->type); } /* if */ goto done; } /* if */ } /* if */ } /* if */ if (!skip_arg_prep) { prep_argument_operand(operand, ptp, (a_conv_descr *)NULL, ec_incompatible_param); } /* if */ expr_arg = make_node_from_operand_for_expr_list(operand); if (*arg_list == NULL) { *arg_list = expr_arg; } else { end_arg_list->next = expr_arg; } /* if */ end_arg_list = expr_arg; ptp = ptp->next; } /* for */ /* Return the type that should result from the call. */ if (bcap->result_type == NULL) { bcap->result_type = dispatch_type; } /* if */ } /* if */ } /* if */ done: return rout; } /* adjust_sync_atomic_builtin */ static a_routine_ptr adjust_builtin_zero_non_value_bits( an_operand *target, an_arg_list_elem_ptr args, a_source_position *closing_paren_position, ARG_UNUSED a_builtin_call_adjustment *bcap, an_expr_node_ptr *arg_list) /* Perform special processing for the __builtin_zero_non_value_bits builtin. The builtin is declared as taking one "void *" argument, but the argument must be a pointer to a complete type and may not be const-qualified. */ { an_operand *operand = NULL; a_boolean err = FALSE; *arg_list = NULL; if (args == NULL) { /* Must have at least one argument. */ expr_pos_error(ec_too_few_arguments, closing_paren_position); err = TRUE; } else if (args->next != NULL) { /* Must have exactly one argument. */ expr_pos_error(ec_too_many_arguments, init_component_pos(args->next)); err = TRUE; } else { operand = operand_of_arg_list_elem(args); if (is_a_glvalue(operand)) { /* A prvalue is needed. */ conv_glvalue_to_prvalue(operand); } /* if */ a_type_ptr arg_type = operand->type; if (!is_pointer_type(arg_type)) { /* Must be pointer type. */ expr_pos_error(ec_expr_not_object_pointer, init_component_pos(args)); err = TRUE; } else { a_type_ptr type = type_pointed_to(arg_type); if (is_void_type(type)) { /* Can't be pointer to void. */ expr_pos_error(ec_expr_not_object_pointer, init_component_pos(args)); err = TRUE; } else if (is_const_qualified_type(arg_type) || is_const_qualified_type(type)) { /* Can't be const-qualified. */ expr_pos_error(ec_cannot_be_const_qualified, init_component_pos(args)); err = TRUE; } /* if */ } /* if */ } /* if */ if (!err) { *arg_list = make_node_from_operand_for_expr_list(operand); } /* if */ return routine_from_function_operand(target); } /* adjust_builtin_zero_non_value_bits */ static a_routine_ptr adjust_overflow_builtin( an_operand *target, an_arg_list_elem_ptr args, a_source_position *closing_paren_position, ARG_UNUSED a_builtin_call_adjustment *bcap, an_expr_node_ptr *arg_list) /* Check the arguments for the generic arithmetic overflow builtin described by target. The operands must be integers or enums (excluding scoped enums in Clang mode). The result must point to a non-const integer, or be an integer for the _p form. GCC excludes bool and enum results; Clang also allows bool and unscoped enums. Use closing_paren_position for missing-argument diagnostics. bcap is unused. Return the routine to call and set *arg_list to the checked argument expressions, or return NULL on error. */ { a_routine_ptr rout = routine_from_function_operand(target); a_boolean err = FALSE; a_boolean result_is_pointer; int arg_index = 0; *arg_list = NULL; result_is_pointer = rout->variant.builtin_function_kind != bfk_add_overflow_p && rout->variant.builtin_function_kind != bfk_sub_overflow_p && rout->variant.builtin_function_kind != bfk_mul_overflow_p; if (args == NULL || args->next == NULL || args->next->next == NULL) { expr_pos_error(ec_too_few_arguments, closing_paren_position); err = TRUE; } else if (args->next->next->next != NULL) { expr_pos_error(ec_too_many_arguments, init_component_pos(args->next->next->next)); err = TRUE; } else { an_arg_list_elem_ptr arg; for (arg = args; arg != NULL; arg = arg->next, ++arg_index) { an_operand_ptr operand; a_type_ptr arg_type; operand = operand_of_arg_list_elem(arg); do_operand_transformations(operand, TOPT_NO_OPTIONS); arg_type = skip_typerefs(operand->type); if (is_error_type(arg_type)) { /* An error argument. */ } else if (arg_index < 2) { if (!is_integral_or_enum_type(arg_type) || (clang_mode && is_scoped_enum_type(arg_type))) { expr_pos_ty_error(ec_invalid_type_for_builtin, init_component_pos(arg), arg_type); err = TRUE; } /* if */ } else { a_type_ptr result_type = arg_type; if (result_is_pointer && is_pointer_type(arg_type)) { result_type = type_pointed_to(arg_type); } /* if */ if ((result_is_pointer && !is_pointer_type(arg_type)) || (clang_mode ? !is_integral_or_unscoped_enum_type(result_type) : (!is_integral_type(result_type) || is_enum_type(result_type) || is_bool_type(result_type))) || (result_is_pointer && is_const_qualified_type(result_type))) { expr_pos_ty_error(ec_invalid_type_for_builtin, init_component_pos(arg), arg_type); err = TRUE; } /* if */ } /* if */ } /* for */ } /* if */ if (err) { rout = NULL; } else { *arg_list = make_expr_list_from_argument_list( args, /*dependent_expression=*/FALSE); } /* if */ return rout; } /* adjust_overflow_builtin */ static a_boolean convert_to_prvalue_and_check_for_identical_types( a_type_ptr tp, an_arg_list_elem_ptr arg) /* Convert the argument to a prvalue (if needed). Return TRUE if the argument type is identical to tp, or if either type is an error type. */ { a_boolean okay = TRUE; an_operand_ptr op = operand_of_arg_list_elem(arg); a_type_ptr arg_type = skip_typerefs(op->type); if (is_a_glvalue(op) && !is_array_type(op->type)) { /* A prvalue is needed. */ conv_glvalue_to_prvalue(op); } /* if */ if (is_error_type(tp) || is_error_type(arg_type)) { /* An error argument. */ } else if (!identical_types(tp, arg_type)) { /* Both arguments must be the same type. */ okay = FALSE; } /* if */ return okay; } /* convert_to_prvalue_and_check_for_identical_types */ static a_routine_ptr adjust_elementwise_or_reduce_builtin( an_operand *target, an_arg_list_elem_ptr args, a_source_position *closing_paren_position, a_builtin_call_adjustment *bcap, an_expr_node_ptr *arg_list) /* Perform special processing for the clang __builtin_elementwise_* and __builtin_reduce_* builtins. Some checking of arguments is performed and the resulting return type is determined for the routine. */ { a_boolean err = FALSE; a_type_ptr return_type = error_type(); a_type_ptr arg_type = NULL, arg2_type = NULL; a_routine_ptr rout = routine_from_function_operand(target); an_operand *op1 = NULL; int n_args = bcap->n_args; *arg_list = NULL; check_assertion(rout != NULL && n_args >= 1 && n_args <= 3); if (args == NULL) { /* Must have at least one argument. */ expr_pos_error(ec_too_few_arguments, closing_paren_position); err = TRUE; } else if (n_args == 1 && args->next != NULL) { /* Must have exactly one argument. */ expr_pos_error(ec_too_many_arguments, init_component_pos(args->next)); err = TRUE; } else if (n_args > 1 && args->next == NULL) { if (rout->variant.builtin_function_kind == bfk_elementwise_clzg || rout->variant.builtin_function_kind == bfk_elementwise_ctzg) { /* The second argument for __builtin_elementwise_clzg and __builtin_elementwise_ctzg is optional. */ n_args = 1; } else { /* Must have more than one argument. */ expr_pos_error(ec_too_few_arguments, closing_paren_position); err = TRUE; } /* if */ } else if (n_args == 2 && args->next->next != NULL) { /* Must have exactly two arguments. */ expr_pos_error(ec_too_many_arguments,init_component_pos(args->next->next)); err = TRUE; } else if (n_args > 2 && args->next->next == NULL) { /* Must have more than two arguments. */ expr_pos_error(ec_too_few_arguments, closing_paren_position); err = TRUE; } else if (n_args == 3 && args->next->next->next != NULL) { /* Must have exactly three arguments. */ expr_pos_error(ec_too_many_arguments, init_component_pos(args->next->next->next)); err = TRUE; } /* if */ if (!err) { op1 = operand_of_arg_list_elem(args); if (is_a_glvalue(op1) && !is_array_type(op1->type)) { /* A prvalue is needed. */ conv_glvalue_to_prvalue(op1); } /* if */ arg_type = skip_typerefs(op1->type); if (bcap->is_elementwise) { /* __builtin_elementwise_* case (with between one and three arguments). The return type is the same as the types of the argument(s). */ return_type = arg_type; if (n_args >= 2) { if (rout->variant.builtin_function_kind == bfk_elementwise_ldexp) { an_operand *op2 = operand_of_arg_list_elem(args->next); check_assertion(n_args == 2); if (is_a_glvalue(op2) && !is_array_type(op2->type)) { /* A prvalue is needed. */ conv_glvalue_to_prvalue(op2); } /* if */ arg2_type = skip_typerefs(op2->type); if (is_error_type(arg_type) || is_error_type(arg2_type)) { /* Don't bother checking; also don't use the type of an erroneous argument in the generated routine type. */ arg2_type = NULL; } else { /* The second argument must be an integer type with the same "shape" as the first argument: both vectors with the same number of elements, or both scalars. */ a_boolean shape_is_okay; #if GNU_VECTOR_TYPES_ALLOWED if (is_vector_type(arg_type) || is_vector_type(arg2_type)) { shape_is_okay = is_vector_type(arg_type) && is_vector_type(arg2_type) && is_integral_type( arg2_type->variant.vector.element_type) && num_vector_elements(arg_type) == num_vector_elements(arg2_type); } else #endif /* GNU_VECTOR_TYPES_ALLOWED */ { shape_is_okay = is_integral_type(arg2_type); } /* if */ if (!shape_is_okay) { expr_pos_error(ec_second_argument_wrong_shape, init_component_pos(args->next)); err = TRUE; } /* if */ } /* if */ } else { an_arg_list_elem_ptr arg = args->next; if (!convert_to_prvalue_and_check_for_identical_types(arg_type, arg)) { if (n_args == 2) { expr_pos_error(ec_both_arguments_must_have_same_type, init_component_pos(args)); } else { expr_pos_error(ec_all_arguments_must_have_same_type, init_component_pos(arg)); } /* if */ err = TRUE; } else if (is_error_type(operand_of_arg_list_elem(arg)->type)) { /* An erroneous second argument was accepted; avoid further processing by setting the error type. */ arg_type = return_type = error_type(); } /* if */ } /* if */ } /* if */ if (!err && n_args >= 3) { an_arg_list_elem_ptr arg = args->next->next; if (!convert_to_prvalue_and_check_for_identical_types(arg_type, arg)) { expr_pos_error(ec_all_arguments_must_have_same_type, init_component_pos(arg)); err = TRUE; } else if (is_error_type(operand_of_arg_list_elem(arg)->type)) { /* An erroneous third argument was accepted; avoid further processing by setting the error type. */ arg_type = return_type = error_type(); } /* if */ } /* if */ } else { /* __builtin_reduce_* case. The return type is the element type of the vector type. */ check_assertion(bcap->is_reduce); if (is_vector_type(arg_type)) { #if GNU_VECTOR_TYPES_ALLOWED return_type = skip_typerefs(arg_type)->variant.vector.element_type; #endif /* GNU_VECTOR_TYPES_ALLOWED */ } else if (is_error_type(arg_type)) { return_type = arg_type; } else { /* Invalid type. */ expr_pos_ty_error(ec_vector_type_required, init_component_pos(args), arg_type); err = TRUE; } /* if */ } /* if */ if (!err) { a_type_ptr type = arg_type; if (is_error_type(type)) { /* No need to check. */ } else { /* Check that the type of the first operand is acceptable. If there's a second operand, we already know that it's acceptable. Some of these builtins can operate on vectors or scalars of the specified type. */ #if GNU_VECTOR_TYPES_ALLOWED if (is_vector_type(type)) { type = skip_typerefs(type)->variant.vector.element_type; } /* if */ #endif /* GNU_VECTOR_TYPES_ALLOWED */ switch (rout->variant.builtin_function_kind) { case bfk_elementwise_abs: err = !(is_signed_integral_type(type) || is_real_floating_type(type)); break; case bfk_elementwise_acos: case bfk_elementwise_asin: case bfk_elementwise_atan: case bfk_elementwise_atan2: case bfk_elementwise_canonicalize: case bfk_elementwise_ceil: case bfk_elementwise_copysign: case bfk_elementwise_cos: case bfk_elementwise_cosh: case bfk_elementwise_exp: case bfk_elementwise_exp2: case bfk_elementwise_floor: case bfk_elementwise_fma: case bfk_elementwise_fmod: case bfk_elementwise_ldexp: case bfk_elementwise_log: case bfk_elementwise_log10: case bfk_elementwise_log2: case bfk_elementwise_maximum: case bfk_elementwise_minimum: case bfk_elementwise_nearbyint: case bfk_elementwise_pow: case bfk_elementwise_rint: case bfk_elementwise_round: case bfk_elementwise_roundeven: case bfk_elementwise_sin: case bfk_elementwise_sinh: case bfk_elementwise_sqrt: case bfk_elementwise_tan: case bfk_elementwise_tanh: case bfk_elementwise_trunc: case bfk_reduce_maximum: case bfk_reduce_minimum: err = !is_real_floating_type(type); break; case bfk_elementwise_max: case bfk_elementwise_min: case bfk_reduce_max: case bfk_reduce_min: err = !(is_integral_type(type) || is_real_floating_type(type)); break; case bfk_reduce_and: case bfk_reduce_or: case bfk_reduce_xor: case bfk_reduce_add: case bfk_reduce_mul: case bfk_elementwise_bitreverse: case bfk_elementwise_popcount: case bfk_elementwise_add_sat: case bfk_elementwise_sub_sat: case bfk_elementwise_fshl: case bfk_elementwise_fshr: case bfk_elementwise_clzg: case bfk_elementwise_ctzg: /* Clang also allows enumeration types. */ err = !is_integral_type(type) && (!clang_mode || !is_enum_type(type)); break; default: unexpected_condition(); } /* switch */ if (err) { expr_pos_ty_error(ec_invalid_type_for_builtin, init_component_pos(args), type); } /* if */ } /* if */ } /* if */ } /* if */ if (err && !expr_stack->suppress_diagnostics) { arg_type = return_type = error_type(); arg2_type = NULL; } /* if */ if (!err || !expr_stack->suppress_diagnostics) { if (n_args >= 2 && arg2_type == NULL) arg2_type = arg_type; a_type_ptr rout_type = make_routine_type(return_type, arg_type, arg2_type, (n_args >= 3) ? arg_type : NULL); /* Create a routine with the desired type. */ a_symbol_ptr sym = builtin_with_particular_type(rout, rout_type); rout = sym->variant.routine.ptr; /* Update the operand: */ an_operand orig_operand = *target; make_function_designator_operand(sym, target->is_qualified_name, /*compiler_generated=*/FALSE, &orig_operand.position, end_position_of_operand(&orig_operand), target->ref_entries_list, target); if (!is_error_operand(target)) { check_assertion(is_expression_operand(target) && is_routine_node(target->variant.expression)); conv_function_designator_to_ptr_to_function(target, (a_source_position *)NULL, /*allow_ctor=*/FALSE, /*will_call=*/TRUE); } /* if */ /* Convert the argument(s). */ *arg_list = make_expr_list_from_argument_list( args, /*dependent_expression=*/FALSE); } /* if */ return rout; } /* adjust_elementwise_or_reduce_builtin */ static a_routine_ptr adjust_type_generic_builtin( an_operand *target, an_arg_list_elem_ptr args, a_source_position *closing_paren_position, a_builtin_call_adjustment *bcap, an_expr_node_ptr *arg_list) /* Perform special processing for the type-generic gcc __builtin_clzg, __builtin_ctzg, __builtin_ffsg, __builtin_parityg, and __builtin_popcountg builtins. Some checking of arguments is performed. */ { a_boolean err = FALSE; a_type_ptr arg1_type = NULL, arg2_type = NULL; a_routine_ptr rout = routine_from_function_operand(target); an_operand *op; *arg_list = NULL; check_assertion(rout != NULL && (bcap->n_args == 1 || bcap->n_args == 2)); if (args == NULL) { /* Must have at least one argument. */ expr_pos_error(ec_too_few_arguments, closing_paren_position); err = TRUE; } else if (bcap->n_args == 1 && args->next != NULL) { /* Must have exactly one argument. */ expr_pos_error(ec_too_many_arguments, init_component_pos(args->next)); err = TRUE; } else if (bcap->n_args == 2 && args->next != NULL && args->next->next != NULL) { /* Must have at most two arguments. */ expr_pos_error(ec_too_many_arguments,init_component_pos(args->next->next)); err = TRUE; } else { op = operand_of_arg_list_elem(args); if (is_a_glvalue(op)) { /* A prvalue is needed. */ conv_glvalue_to_prvalue(op); } /* if */ arg1_type = skip_typerefs(op->type); if (args->next != NULL) { op = operand_of_arg_list_elem(args->next); if (is_a_glvalue(op)) { /* A prvalue is needed. */ conv_glvalue_to_prvalue(op); } /* if */ arg2_type = skip_typerefs(op->type); } /* if */ if (!is_error_type(arg1_type)) { switch (rout->variant.builtin_function_kind) { case bfk_ffsg: err = !is_integral_type(arg1_type) || !is_signed_integral_type(arg1_type); break; case bfk_parityg: err = !is_integral_type(arg1_type) || is_signed_integral_type(arg1_type); break; case bfk_clzg: case bfk_ctzg: case bfk_popcountg: err = !is_integral_type(arg1_type) || is_signed_integral_type(arg1_type); if (err && clang_mode) { a_type_ptr tp = arg1_type; #if GNU_VECTOR_TYPES_ALLOWED if (is_vector_type(tp)) tp = tp->variant.vector.element_type; if (is_bool_type(tp)) { /* Clang allows bool (or vectors of bool) type for the argument. */ err = FALSE; } /* if */ #endif /* GNU_VECTOR_TYPES_ALLOWED */ if (is_enum_type(tp) && !is_signed_integral_type(tp)) { /* Clang also allows enumeration types with an unsigned underlying type. */ err = FALSE; } /* if */ } /* if */ break; default: unexpected_condition_str( "adjust_type_generic_builtin: bad builtin function kind"); } /* switch */ if (err) { expr_pos_ty_error(ec_invalid_type_for_builtin, init_component_pos(args), arg1_type); } /* if */ } /* if */ /* The optional second argument must have type int. */ if (!err && arg2_type != NULL && !is_error_type(arg2_type) && (!is_integral_type(arg2_type) || arg2_type->variant.integer.int_kind != ik_int)) { expr_pos_ty_error(ec_invalid_type_for_builtin, init_component_pos(args->next), arg2_type); err = TRUE; } /* if */ } /* if */ if (!err) { a_type_ptr rout_type = make_routine_type(integer_type(ik_int), arg1_type, arg2_type); /* Create a routine with the desired type. */ a_symbol_ptr sym = builtin_with_particular_type(rout, rout_type); rout = sym->variant.routine.ptr; /* Update the operand: */ an_operand orig_operand = *target; make_function_designator_operand(sym, target->is_qualified_name, /*compiler_generated=*/FALSE, &orig_operand.position, end_position_of_operand(&orig_operand), target->ref_entries_list, target); if (!is_error_operand(target)) { check_assertion(is_expression_operand(target) && is_routine_node(target->variant.expression)); conv_function_designator_to_ptr_to_function(target, (a_source_position *)NULL, /*allow_ctor=*/FALSE, /*will_call=*/TRUE); } /* if */ /* Convert the argument(s). */ *arg_list = make_expr_list_from_argument_list( args, /*dependent_expression=*/FALSE); } /* if */ return rout; } /* adjust_type_generic_builtin */ static a_routine_ptr adjust_return_type_to_type_of_first_argument( an_operand *target, an_arg_list_elem_ptr args, a_source_position *closing_paren_position, a_builtin_call_adjustment *bcap, an_expr_node_ptr *arg_list) /* Perform special processing for builtins that take exactly one argument and whose return type is the same as the type of the first argument. */ { a_type_ptr arg1_type; a_routine_ptr rout = routine_from_function_operand(target); an_operand *op1; *arg_list = NULL; check_assertion(rout != NULL && bcap->n_args == 1); if (args == NULL) { /* Must have at least one argument. */ expr_pos_error(ec_too_few_arguments, closing_paren_position); } else if (args->next != NULL) { /* Must have exactly one argument. */ expr_pos_error(ec_too_many_arguments, init_component_pos(args->next)); } else { op1 = operand_of_arg_list_elem(args); if (is_a_glvalue(op1)) { /* A prvalue is needed. */ conv_glvalue_to_prvalue(op1); } /* if */ arg1_type = skip_typerefs(op1->type); a_type_ptr rout_type = make_routine_type(arg1_type, arg1_type, (a_type_ptr)NULL, (a_type_ptr)NULL, (a_type_ptr)NULL, (a_type_ptr)NULL); /* Create a routine with the desired type. */ a_symbol_ptr sym = builtin_with_particular_type(rout, rout_type); rout = sym->variant.routine.ptr; /* Update the operand: */ an_operand orig_operand = *target; make_function_designator_operand(sym, target->is_qualified_name, /*compiler_generated=*/FALSE, &orig_operand.position, end_position_of_operand(&orig_operand), target->ref_entries_list, target); if (!is_error_operand(target)) { check_assertion(is_expression_operand(target) && is_routine_node(target->variant.expression)); conv_function_designator_to_ptr_to_function(target, (a_source_position *)NULL, /*allow_ctor=*/FALSE, /*will_call=*/TRUE); } /* if */ /* Convert the argument. */ *arg_list = make_node_from_operand_for_expr_list(op1); } /* if */ return rout; } /* adjust_return_type_to_type_of_first_argument */ static a_routine_ptr adjust_masked_builtin( an_operand *target, an_arg_list_elem_ptr args, a_source_position *closing_paren_position, a_builtin_call_adjustment *bcap, an_expr_node_ptr *arg_list) /* Perform special processing for some "masked" builtins whose return type is dependent on the argument types. Note that only "masked" builtins whose return type is dependent are handled here (the others use the default void(...) signature so no argument checking is done). *target describes the function specified in the call; on return it is updated to describe the appropriate concrete function based on the argument types. args gives the argument list. *closing_paren_position gives the position of the final ")" in the argument list. bcap points to a data structure describing the adjustment to be made. On return, *arg_list is set to point to the argument list in expression form. */ { a_routine_ptr rout = routine_from_function_operand(target); #if GNU_VECTOR_TYPES_ALLOWED an_operand *op1; *arg_list = NULL; check_assertion(rout != NULL && (bcap->n_args == 2 || bcap->n_args == 3)); if (args == NULL) { /* Must have at least two arguments. */ expr_pos_error(ec_too_few_arguments, closing_paren_position); goto done; } else if (args->next == NULL) { /* Must have at least two arguments. */ expr_pos_error(ec_too_few_arguments, closing_paren_position); goto done; } else if (args->next->next != NULL) { if (bcap->n_args == 3 || rout->variant.builtin_function_kind == bfk_masked_load) { /* __builtin_masked_load can have an optional third argument. */ if (args->next->next->next != NULL) { expr_pos_error(ec_too_many_arguments, init_component_pos(args->next->next->next)); goto done; } /* if */ } else { expr_pos_error(ec_too_many_arguments, init_component_pos(args->next->next)); goto done; } /* if */ } /* if */ { a_type_ptr arg1_type, arg2_type, arg3_type = NULL; op1 = operand_of_arg_list_elem(args); if (is_a_glvalue(op1)) { /* A prvalue is needed. */ conv_glvalue_to_prvalue(op1); } /* if */ arg1_type = skip_typerefs(op1->type); if (is_error_type(arg1_type)) { goto done; } /* if */ if (!is_vector_type(arg1_type) || !is_bool_type(arg1_type->variant.vector.element_type)) { expr_pos_error(ec_vector_of_boolean_required, init_component_pos(args)); goto done; } /* if */ a_type_ptr return_type, underlying_element; an_operand *op2 = operand_of_arg_list_elem(args->next); if (is_a_glvalue(op2)) { /* A prvalue is needed. */ conv_glvalue_to_prvalue(op2); } /* if */ arg2_type = skip_typerefs(op2->type); if (is_error_type(arg2_type)) { goto done; } /* if */ if (rout->variant.builtin_function_kind == bfk_masked_gather) { /* A vector (of the same shape as the first argument) is required. */ if (!is_vector_type(arg2_type) || num_vector_elements(arg1_type) != num_vector_elements(arg2_type)) { expr_pos_error(ec_vector_type_with_size_is_required, init_component_pos(args->next)); goto done; } /* if */ underlying_element = arg2_type->variant.vector.element_type; } else { /* A pointer to a scalar type is required for the second argument. */ if (!is_pointer_type(arg2_type) || !is_scalar_type(type_pointed_to(arg2_type))) { expr_pos_error(ec_pointer_to_scalar_required, init_component_pos(args->next)); goto done; } /* if */ underlying_element = type_pointed_to(arg2_type); } /* if */ /* The return type is a vector with the same shape as the first argument, but with an underlying type that depends on the second argument. */ return_type = make_vector_type(underlying_element, num_vector_elements(arg1_type), arg1_type->variant.vector.kind); if (args->next->next != NULL) { an_operand *op3 = operand_of_arg_list_elem(args->next->next); if (is_a_glvalue(op3)) { /* A prvalue is needed. */ conv_glvalue_to_prvalue(op3); } /* if */ arg3_type = skip_typerefs(op3->type); if (is_error_type(arg3_type)) { goto done; } /* if */ if (rout->variant.builtin_function_kind == bfk_masked_gather) { /* The third argument should be a pointer to the appropriate scalar type. */ if (!is_pointer_type(arg3_type) || !identical_types(type_pointed_to(arg3_type), arg2_type->variant.vector.element_type)) { expr_pos_error(ec_pointer_to_scalar_required, init_component_pos(args->next->next)); goto done; } /* if */ } else { /* The type of the third argument should match the return type. */ if (!identical_types(return_type, arg3_type)) { expr_pos_ty_error(ec_incorrect_masked_type, init_component_pos(args->next->next), return_type); goto done; } /* if */ } /* if */ } /* if */ a_type_ptr rout_type = make_routine_type(return_type, arg1_type, arg2_type, arg3_type, (a_type_ptr)NULL, (a_type_ptr)NULL, (a_type_ptr)NULL); /* Create a routine with the desired type. */ a_symbol_ptr sym = builtin_with_particular_type(rout, rout_type); rout = sym->variant.routine.ptr; /* Update the operand: */ an_operand orig_operand = *target; make_function_designator_operand(sym, target->is_qualified_name, /*compiler_generated=*/FALSE, &orig_operand.position, end_position_of_operand(&orig_operand), target->ref_entries_list, target); if (!is_error_operand(target)) { check_assertion(is_expression_operand(target) && is_routine_node(target->variant.expression)); conv_function_designator_to_ptr_to_function(target, (a_source_position *)NULL, /*allow_ctor=*/FALSE, /*will_call=*/TRUE); } /* if */ /* Convert the arguments. */ *arg_list = make_expr_list_from_argument_list( args, /*dependent_expression=*/FALSE); } done: #endif /* GNU_VECTOR_TYPES_ALLOWED */ return rout; } /* adjust_masked_builtin */ static a_routine_ptr adjust_srcloc_builtin( an_operand *target, an_arg_list_elem_ptr args, a_source_position *closing_paren_position, a_builtin_call_adjustment *bcap, an_expr_node_ptr *arg_list) /* Perform special processing for the __builtin_source_location builtin. This is used in Clang mode to set the correct return type. */ { a_routine_ptr rout = routine_from_function_operand(target); a_type_ptr rout_type = make_routine_type(bcap->result_type, (a_type_ptr)NULL, (a_type_ptr)NULL, (a_type_ptr)NULL, (a_type_ptr)NULL, (a_type_ptr)NULL); check_assertion(rout != NULL); rout->type = rout_type; return rout; } /* adjust_srcloc_builtin */ #endif /* BUILTIN_FUNCTIONS_ENABLED */ static void report_this_param_mismatch( a_routine_ptr routine, a_type_ptr this_param_type, an_operand *bound_function_selector, an_arg_match_summary *this_match) /* A selector (bound_function_selector) doesn't match a call to a member function (routine designates the member if it is named but is otherwise NULL) because of a mismatch in type qualifiers or value category. Issue an error describing the mismatch. this_param_type is the type of the hidden *this parameter (a reference; not a pointer), and *this_match describes the result of the failed match. */ { if (expr_error_should_be_issued()) { if (this_match->ref_qualifier == (a_ref_qualifier_kind)rqk_rvalue && is_an_lvalue(bound_function_selector)) { /* An lvalue with an rvalue ref-qualifier. */ if (routine != NULL) { pos_sy_error(ec_cannot_call_named_member_on_lvalue, &bound_function_selector->position, symbol_for(routine)); } else { pos_error(ec_cannot_call_member_on_lvalue, &bound_function_selector->position); } /* if */ } else if (this_match->ref_qualifier == (a_ref_qualifier_kind)rqk_lvalue && is_an_rvalue(bound_function_selector) && !is_reference_that_can_bind_to_rvalue(this_param_type)) { /* An rvalue with an lvalue ref-qualifier and a non-const member function. */ if (routine != NULL) { pos_sy_error(ec_cannot_call_named_member_on_rvalue, &bound_function_selector->position, symbol_for(routine)); } else { pos_error(ec_cannot_call_member_on_rvalue, &bound_function_selector->position); } /* if */ } else { /* Not a value category mismatch. It must therefore be a qualifier mismatch (e.g., calling a non-const member on a const value). */ a_diagnostic_ptr dp; if (routine != NULL) { /* The member function called is known. */ dp = pos_sy_start_error(ec_unqual_named_function_with_qual_object, &bound_function_selector->position, symbol_for(routine)); } else { dp = pos_start_error(ec_unqual_function_with_qual_object, &bound_function_selector->position); } /* if */ display_object_type(bound_function_selector->type, dp); end_diagnostic(dp); } /* if */ } /* if */ } /* report_this_param_mismatch */ static void turn_mem_func_operand_into_unknown_function(an_operand *opnd) /* The given operand represents a specific member function. Turn it into a template-dependent unknown function (i.e., a tpck_unknown_function constant operand). */ { a_constant_ptr uf_con; a_symbol_ptr sym = opnd->symbol; uf_con = fs_constant((a_constant_repr_kind)ck_template_param); set_template_param_constant_kind( uf_con, (a_template_param_constant_kind)tpck_unknown_function); uf_con->type = type_of_unknown_templ_param_nontype; uf_con->variant.template_param.is_qualified_name = opnd->is_qualified_name; uf_con->variant.template_param.variant.unknown_function.opname_kind = sym->header->variant.opname; set_source_corresp_name(&uf_con->source_corresp, sym->header); if (sym->is_class_member) { set_class_membership((a_symbol_ptr)NULL, &uf_con->source_corresp, sym_parent_class(sym)); } else { a_namespace_ptr parent_nsp = sym_parent_namespace(sym); if (parent_nsp != NULL) { set_namespace_membership((a_symbol_ptr)NULL, &uf_con->source_corresp, parent_nsp); } /* if */ } /* if */ make_constant_operand(uf_con, opnd); } /* turn_mem_func_operand_into_unknown_function */ static a_boolean lambda_closure_has_nondependent_call_type(a_type_ptr ctp) /* ctp is a lambda closure type. Return TRUE only if the associated call operator has a nondependent type, ignoring the "is_nonreal_class" flag on the closure type itself. */ { a_routine_ptr rp = lambda_body_for_closure(ctp); a_boolean result, is_nonreal; is_nonreal = ctp->variant.class_struct_union.is_nonreal_class; if (is_nonreal) { ctp->variant.class_struct_union.is_nonreal_class = FALSE; } /* if */ result = rp != NULL && !is_template_dependent_type(rp->type); if (is_nonreal) { ctp->variant.class_struct_union.is_nonreal_class = is_nonreal; } /* if */ return result; } /* lambda_closure_has_nondependent_call_type */ #if BUILTIN_FUNCTIONS_ENABLED static void adjust_invoke_pointer_to_member_object( an_operand *object_operand, a_type_ptr member_class_type, a_boolean *selector_is_object_pointer) /* Adjust *object_operand, the object to which a pointer-to-member is applied by __builtin_invoke, following the rules of std::invoke ([func.require]). member_class_type is the class of which the pointer-to-member is a member. If the (decayed) type of *object_operand is that class or a class derived from it, the pointer-to-member is applied to the object directly. Otherwise, if it is a std::reference_wrapper specialization, the object is replaced with the result of its get() member function. Otherwise the object is dereferenced: a pointer selects the "->*" form (so *selector_is_object_pointer is set to TRUE), and any other type has its built-in or overloaded "*" operator applied. Whether the resulting object is suitable as the left operand of the selection is left for the caller to check when it forms the selection. */ { a_type_ptr obj_tp = remove_cvref(object_operand->type), uobj_tp = skip_typerefs(obj_tp); *selector_is_object_pointer = FALSE; if (is_immediate_class_type(uobj_tp) && is_same_class_or_base_class_thereof(uobj_tp, member_class_type)) { /* The object is the member's class or a class derived from it; the pointer-to-member applies to it directly. */ } else if (is_std_class(uobj_tp, "reference_wrapper")) { /* "Unwrap" a std::reference_wrapper operand by calling its "get()". */ call_named_member_function(object_operand, "get", (a_template_arg*)NULL, (an_arg_list_elem_ptr)NULL, object_operand, object_operand); } else if (is_pointer_type(uobj_tp)) { /* A pointer object selects through the "->*" form. */ conv_glvalue_to_prvalue(object_operand); *selector_is_object_pointer = TRUE; } else if (is_immediate_class_type(uobj_tp) || is_immediate_enum_type(uobj_tp)) { /* A potentially pointer-like object: Select through "*object", applying any user-defined indirection operator. */ a_source_position star_position = object_operand->position; a_boolean processed = FALSE; an_operand deref_operand; check_for_operator_overloading(onk_star, /*unary_operator=*/TRUE, /*must_be_member_function=*/FALSE, /*try_conversions=*/TRUE, /*has_predef_meaning=*/FALSE, object_operand, (an_operand*)NULL, &star_position, curr_token_sequence_number, (a_nondependent_call_depth)0, (a_source_position*)NULL, &deref_operand, &processed); copy_operand(&deref_operand, object_operand); } /* if */ } /* adjust_invoke_pointer_to_member_object */ static a_boolean adjust_builtin_call_arguments( a_builtin_call_adjustment *bcap, a_routine_ptr operand_routine, an_operand *bound_function_selector, a_source_position *call_position, a_source_position *closing_paren_position, an_operand **p_operand, an_arg_list_elem_ptr *p_arg_list, an_expr_node_ptr *p_argument_list, a_type_ptr *p_routine_type, a_routine_ptr *p_routine, a_boolean *p_overloaded_function_case, a_symbol_ptr *p_overloaded_function_symbol, a_boolean *p_unknown_dependent_function, a_boolean *p_try_surrogate_functions, a_boolean *p_has_overloaded_call_operator, an_arg_list_elem_ptr *p_delay_free_arg_list_elem, an_operand *result) /* Adjust the arguments of a call of a predeclared builtin function that needs compiler "magic", as described by *bcap. This handles __builtin_invoke (bcap->is_invoke) and builtins that supply an argument-checking callback (bcap->callback). *p_operand is the function operand of the call, which may be replaced (e.g., by the selected member of an overload set); the remaining arguments are *p_arg_list, with the already-checked arguments in *p_argument_list. The other p_* parameters hold the evolving state of the call (the routine and routine type being called, whether overload resolution is needed, etc.) and are updated in place. operand_routine is the routine for the builtin operand itself, bound_function_selector receives any implied selector, and call_position and closing_paren_position give source positions for the call and its closing parenthesis. The result is returned in *result when this routine fully handles the construct (in particular, the pointer-to-data-member form of __builtin_invoke, which is just a member selection rather than a call). Return TRUE in that case, so that the caller stops processing the call; return FALSE when the caller should continue forming the call as usual. */ { a_boolean done = FALSE; an_operand *operand = *p_operand; an_arg_list_elem_ptr arg_list = *p_arg_list; an_expr_node_ptr argument_list = *p_argument_list; a_type_ptr routine_type = *p_routine_type; a_routine_ptr routine = *p_routine; a_boolean overloaded_function_case = *p_overloaded_function_case; a_symbol_ptr overloaded_function_symbol = *p_overloaded_function_symbol; a_boolean unknown_dependent_function = *p_unknown_dependent_function; a_boolean try_surrogate_functions = *p_try_surrogate_functions; a_boolean has_overloaded_call_operator = *p_has_overloaded_call_operator; an_arg_list_elem_ptr delay_free_arg_list_elem = *p_delay_free_arg_list_elem; if (arg_list_is_type_dependent(arg_list)) { /* Defer handling of dependent calls. */ overloaded_function_case = TRUE; overloaded_function_symbol = symbol_for(operand_routine); routine = NULL; routine_type = NULL; unknown_dependent_function = TRUE; } else if (bcap->is_invoke) { /* Special handling for __builtin_invoke. */ an_operand_ptr first_operand; if (arg_list == NULL) { expr_pos_error(ec_too_few_arguments, closing_paren_position); make_error_operand(result); done = TRUE; goto write_back; } /* if */ check_arg_list_elem_is_expression(arg_list); first_operand = operand_of_arg_list_elem(arg_list); do_operand_transformations(first_operand, TOPT_WILL_CALL | TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION); if (first_operand->kind == ok_error) { make_error_operand(result); done = TRUE; goto write_back; } else { an_arg_list_elem_ptr first_arg = arg_list; routine_type = NULL; arg_list = arg_list->next; if (is_class_struct_union_type(first_operand->type)) { /* For a class-type operand, look for a function call operator. */ a_type_ptr class_type = skip_typerefs(first_operand->type); a_symbol_ptr member_function_symbol; complete_class_type_is_needed(class_type); try_surrogate_functions = TRUE; overloaded_function_case = TRUE; operand = first_operand; copy_operand(first_operand, bound_function_selector); member_function_symbol = opname_member_function_symbol( onk_function_call, class_type); if (member_function_symbol != NULL) { /* There is an operator() function. The operand has become the selector object, and the function call operator routine becomes the operand. We can use an indefinite function operand whether the operator() is overloaded or not. */ has_overloaded_call_operator = TRUE; make_indefinite_function_operand(member_function_symbol, (a_symbol_locator*)NULL, first_operand); overloaded_function_symbol = member_function_symbol; bind_member_function_operand_to_selector( bound_function_selector, /*selector_is_object_pointer=*/FALSE, first_operand); } /* if */ } else { /* We should have either a pointer or a pointer to member. */ a_boolean mbr_pointer = is_ptr_to_member_type(first_operand->type); if (mbr_pointer || is_pointer_type(first_operand->type)) { overloaded_function_case = FALSE; conv_glvalue_to_prvalue(first_operand); if (mbr_pointer) { routine_type = pm_member_type(first_operand->type); } else { routine_type = type_pointed_to(first_operand->type); } /* if */ if (!type_is(skip_typerefs(routine_type), tk_routine)) { /* The "function" being invoked is not a (pointer-to-) function. For a pointer to a data member, __builtin_invoke(pmd, obj) denotes the member access obj.*pmd; anything else is an error. */ if (!mbr_pointer) { free_arg_list_elem(first_arg); if (expr_error_should_be_issued()) { pos_error(ec_expr_not_ptr_to_function, &first_operand->position); } /* if */ make_error_operand(result); done = TRUE; goto write_back; } /* if */ /* Pointer to data member. The remaining arguments must consist of exactly the object to which the pointer-to-member is applied. */ { an_operand_ptr object_operand; if (arg_list == NULL) { free_arg_list_elem(first_arg); if (expr_error_should_be_issued()) { expr_pos_error(ec_too_few_arguments, closing_paren_position); } /* if */ make_error_operand(result); done = TRUE; goto write_back; } /* if */ check_arg_list_elem_is_expression(arg_list); object_operand = operand_of_arg_list_elem(arg_list); if (arg_list->next != NULL) { free_arg_list_elem(first_arg); if (expr_error_should_be_issued()) { expr_pos_error(ec_too_many_arguments, closing_paren_position); } /* if */ make_error_operand(result); done = TRUE; goto write_back; } else { /* Build the member access "obj.*pmd", adjusting the object operand the way std::invoke does. */ a_boolean object_is_pointer; an_operand selector; adjust_invoke_pointer_to_member_object( object_operand, pm_class_type(first_operand->type), &object_is_pointer); if (is_error_operand(object_operand)) { make_error_operand(result); } else { /* The selector is unused: a data-member selection never produces a bound function. */ combine_ptr_to_member_operands(object_operand, first_operand, object_is_pointer, call_position, result, &selector); } /* if */ set_operand_position(result, call_position, closing_paren_position, call_position); free_arg_list_elem(arg_list); free_arg_list_elem(first_arg); arg_list = NULL; done = TRUE; goto write_back; } /* if */ } } else if (mbr_pointer) { /* For a pointer to member, the second operand should be of class type (or pointer to class type). */ an_operand_ptr second_operand; a_type_ptr cls_type; a_boolean selector_is_object_pointer; if (arg_list == NULL) { free_arg_list_elem(first_arg); if (expr_error_should_be_issued()) { expr_pos_error(ec_too_few_arguments, closing_paren_position); } /* if */ make_error_operand(result); done = TRUE; goto write_back; } /* if */ check_arg_list_elem_is_expression(arg_list); second_operand = operand_of_arg_list_elem(arg_list); /* Adjust the object operand before freeing the pointer-to-member argument: a user-defined "*" operator applied while adjusting the object would otherwise reuse the just-freed argument slot still referenced by first_operand. */ adjust_invoke_pointer_to_member_object( second_operand, pm_class_type(first_operand->type), &selector_is_object_pointer); free_arg_list_elem(first_arg); first_arg = arg_list; cls_type = selector_is_object_pointer ? type_pointed_to(second_operand->type) : second_operand->type; if (is_error_operand(second_operand)) { make_error_operand(result); done = TRUE; goto write_back; } /* if */ if (!is_class_struct_union_type(cls_type)) { if (expr_error_should_be_issued()) { expr_pos_ty_error(ec_expr_not_class, &second_operand->position, cls_type); } /* if */ make_error_operand(result); done = TRUE; goto write_back; } /* if */ copy_operand(second_operand, bound_function_selector); bind_member_function_operand_to_selector( bound_function_selector, selector_is_object_pointer, first_operand); arg_list = arg_list->next; } /* if */ operand = first_operand; } else { free_arg_list_elem(first_arg); if (expr_error_should_be_issued()) { pos_error(ec_expr_not_ptr_to_function, &first_operand->position); } /* if */ make_error_operand(result); done = TRUE; goto write_back; } /* if */ } /* if */ delay_free_arg_list_elem = first_arg; } /* if */ if (!overloaded_function_case) { /* In the non-overloaded case, check and transform the call arguments based on the parameter list. */ an_arg_check_block arg_block; start_call_argument_processing(routine_type, NULL, &arg_block); arg_block.closing_paren_position = *closing_paren_position; process_call_argument_list(arg_list, &arg_block); argument_list = arg_block.argument_head; free_init_component_list(arg_list); arg_list = NULL; } /* if */ } else if (bcap->callback != nullptr) { /* Check and adjust the arguments for a call of a builtin function. An argument can still be a brace-enclosed list here, and no callback accepts that, so diagnose such arguments and replace them with error expressions; the callbacks can then assume that every argument is an expression. Also determine the concrete routine being called, based on the argument types. */ an_arg_list_elem_ptr ap; for (ap = arg_list; ap != NULL; ap = next_elem(ap)) { check_arg_list_elem_is_expression(ap); } /* for */ routine = bcap->callback(operand, arg_list, closing_paren_position, bcap, &argument_list); if (routine == NULL) { /* Something went wrong. */ expr_expect_error(); make_error_operand(result); done = TRUE; goto write_back; } /* if */ routine_type = routine->type; } /* if */ write_back: *p_operand = operand; *p_arg_list = arg_list; *p_argument_list = argument_list; *p_routine_type = routine_type; *p_routine = routine; *p_overloaded_function_case = overloaded_function_case; *p_overloaded_function_symbol = overloaded_function_symbol; *p_unknown_dependent_function = unknown_dependent_function; *p_try_surrogate_functions = try_surrogate_functions; *p_has_overloaded_call_operator = has_overloaded_call_operator; *p_delay_free_arg_list_elem = delay_free_arg_list_elem; return done; } /* adjust_builtin_call_arguments */ #endif /* BUILTIN_FUNCTIONS_ENABLED */ static void scan_function_call(an_operand *operand, an_operand *bound_function_selector, a_boolean is_multi_subscript, a_rescan_control_block *rcblock, an_operand *result, /* Defaulted: */ an_operand *p_subscript) /* Scan a function call (if is_multi_subscript is FALSE) or a C++23 multi- subscript construct (if is_multi_subscript is TRUE). When is_multi_subscript is FALSE, the function to be called is given by *operand, modified by *bound_function_selector if the function is bound. Even if the function is not bound, bound_function_selector points to an operand that can be filled in if an implicit selector is generated. When is_multi_subscript is TRUE, operand is the expression that is being subscripted (of class type or of a template-dependent type). The current token is the "(" or "[" of the beginning of the argument list. Scan the arguments, make a call expression, and return an operand for it in *result, except that when is_multi_subscript is TRUE the subscript is either returned to *p_subscript when the subscript operation was successfully processed as a member call, or *p_subscript is left unchanged if the subscript should be handled the traditional way. If rcblock is non-NULL, redo semantic analysis on a previously-scanned expression, and return the result in *result (or an error indication in *rcblock). operand and bound_function_selector are expected to be NULL in that case. */ { an_expr_node_ptr argument_list, expr; a_type_ptr routine_type = NULL; a_type_ptr orig_routine_type = NULL; a_symbol_ptr overloaded_function_symbol = NULL; a_boolean overloaded_function_case = FALSE; a_boolean vacuous_destructor_case = FALSE; a_source_position call_position, first_arg_position; a_source_position start_position, operator_position, closing_paren_position; an_operand local_bound_function_selector, local_operand; an_arg_match_summary this_match_summary; an_arg_list_elem_ptr arg_list = NULL; a_routine_ptr routine = NULL; a_boolean already_after_left_paren = FALSE; an_expr_operator_kind op; a_boolean try_surrogate_functions = FALSE; a_token_sequence_number opening_paren_tok_seq_number; a_boolean unknown_dependent_function = FALSE; a_boolean handle_case = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED a_boolean implicit_delegate_invocation = FALSE; a_boolean ignore_call = FALSE; a_boolean saved_evaluated = FALSE; a_boolean saved_potentially_evaluated = FALSE; an_expr_node_ptr castexp; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ a_boolean call_folded_to_constant = FALSE; #if BUILTIN_FUNCTIONS_ENABLED a_routine_ptr operand_routine; a_builtin_call_adjustment bca, *bcap = NULL; an_arg_list_elem_ptr delay_free_arg_list_elem = NULL; #endif /* BUILTIN_FUNCTIONS_ENABLED */ a_boolean call_may_be_folded = FALSE; a_boolean do_arg_dep_lookup = FALSE; a_boolean arg_dep_lookup_suppressed = FALSE; a_boolean adl_suppressed_by_qualification = FALSE; a_boolean found_through_adl = FALSE; a_boolean has_overloaded_call_operator = FALSE; a_boolean member_of_proto_inst = FALSE; a_boolean saved_uses_this_operand = expr_stack->uses_this_operand; an_expression_kind saved_expr_kind = expr_stack->expression_kind; an_overload_context ovl_context = oc_default; db_enter(4, "scan_function_call"); /* While processing this call, ensure that the "uses_this_operand" only reflects uses of "this" within the current call. */ expr_stack->uses_this_operand = FALSE; if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. */ check_assertion(rcblock->operator_token == tok_lparen); check_assertion(operand == NULL && bound_function_selector == NULL); operand = &local_operand; bound_function_selector = &local_bound_function_selector; make_call_rescan_operands(rcblock, operand, bound_function_selector, &operator_position, &opening_paren_tok_seq_number, &closing_paren_position); if (rcblock->error_detected) { /* If there were suppressed errors, the call is unreliable and further substitutions are not helpful. */ make_error_operand(result); goto done; } /* if */ } else { /* Normal, non-rescan, processing. */ /* Save the position of the "(". */ operator_position = pos_curr_token; opening_paren_tok_seq_number = curr_token_sequence_number; } /* if */ call_position = operand->position; /* If the operand is a bound function, the start position of the call is the start of the selector. Watch out for pointer to member calls like (x->*y)(z), where the position of the selector is later than the position of the operand, because the latter includes the left parenthesis. */ start_position = (operand->bound_function && bound_function_selector->position.seq != 0 && compare_source_positions( &bound_function_selector->position, &call_position) < 0) ? bound_function_selector->position : call_position; #if MICROSOFT_EXTENSIONS_ALLOWED if (is_event_ref_operand(operand)) { /* Invoking an event is equivalent to calling its "raise" accessor. */ rewrite_event_ref_for_call(operand, bound_function_selector); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Argument-dependent lookup will be done if the function name is a simple name followed by a left parenthesis (not, for example, a name enclosed in parentheses as in "(f)(x)"). */ if (!C_mode() && arg_dependent_lookup_enabled && !operand->bound_function) { if (operand->is_name_followed_by_left_paren && !operand->is_qualified_name) { do_arg_dep_lookup = TRUE; if (gpp_mode && gnu_version < 40400 && operand->is_template_id) { /* g++ does not do argument-dependent lookup when an explicit template argument list is specified. Fixed in 4.4. */ do_arg_dep_lookup = FALSE; } /* if */ } else { a_boolean routine_operand = is_undefined_symbol_operand(operand) || is_indefinite_function_operand(operand); a_symbol_ptr rout_sym = NULL; if (routine_operand) { rout_sym = operand->symbol; } else if (is_expression_operand(operand)) { /* Check for a resolved function. */ an_expr_node_ptr targ_expr = skip_parens(operand->variant.expression); if (is_routine_node(targ_expr)) { routine_operand = TRUE; rout_sym = symbol_for(targ_expr->variant.routine.ptr); } /* if */ } /* if */ if (routine_operand) { /* For the cases that are simple names, record that argument dependent lookup was suppressed. Note that non-overloaded functions come here as overloaded functions when argument-dependent lookup is enabled. The flag here only indicates suppression because of the lack of an immediately following left parenthesis. */ if (!operand->is_name_followed_by_left_paren) { /* Argument-dependent lookup does not apply to member functions, so don't record it as "suppressed" for them. */ if (!rout_sym->is_class_member) { arg_dep_lookup_suppressed = TRUE; } /* if */ } else if (operand->is_qualified_name) { adl_suppressed_by_qualification = TRUE; } /* if */ } /* if */ } /* if */ } /* if */ #if BUILTIN_FUNCTIONS_ENABLED if (builtin_functions_enabled && !curr_expr_kind_is(ek_pp)) { /* Some GNU-style built-in functions may be treated as constant expressions. Among these folded built-ins are some whose argument processing is different from that done for function calls. Such pseudo-calls are fully handled by the call to scan_builtin_pseudo_call. */ a_boolean pseudo_call; check_builtin_function_for_call(operand, &call_may_be_folded, &pseudo_call); if (pseudo_call) { check_assertion(call_may_be_folded); scan_builtin_pseudo_call(operand, rcblock, result); goto done; } /* if */ if (call_may_be_folded && !constexpr_enabled && (int)expr_stack->expression_kind < (int)ek_normal) { /* Function calls are normally not permitted in "traditional" (i.e., pre-constexpr) constant expression contexts. However, some calls to built-in functions are permitted and if they are, other constraints on traditional constant expressions -- such as requiring integral types -- are temporarily suspended. That allows cases such as: int xxx[__builtin_strlen("xxx")]; where the operand to the call is not of integral type. */ expr_stack->expression_kind = ek_normal; expr_stack->favor_constant_result = TRUE; } /* if */ } /* if */ #endif /* BUILTIN_FUNCTIONS_ENABLED */ if (!call_may_be_folded && curr_expr_kind_is_const() && (!constexpr_enabled || curr_expr_kind_is(ek_pp))) { if (microsoft_mode && curr_expr_kind_is(ek_pp)) { /* MSVC just warns about apparent function calls in preprocessor expressions and flushes to the end of the line. */ expr_pos_warning(ec_bad_constant_function_call, &operand->position); flush_to_newline(); make_zero_operand(operand, integer_type(ik_long)); goto done; } else { /* Routine calls that cannot be folded should not appear in constant- expressions. */ error_in_operand(ec_bad_constant_function_call, operand); } /* if */ } else if (is_expression_operand(operand) && (expr = skip_parens(operand->variant.expression), is_operation_node(expr)) && (op = expr->variant.operation.kind, (op == (an_expr_operator_kind)eok_dot_vacuous_destructor_call || op == (an_expr_operator_kind) eok_points_to_vacuous_destructor_call))) { /* This operand was generated from a vacuous destructor call, e.g., p->int::~int(). */ vacuous_destructor_case = TRUE; /* routine = NULL; -- already set. */ if (rcblock != NULL) { first_arg_position = closing_paren_position; } else { /* Move to after the left parenthesis. */ (void)get_token(); first_arg_position = pos_curr_token; } /* if */ already_after_left_paren = TRUE; } else if (!C_mode() && (is_class_struct_union_type(operand->type) #if MICROSOFT_EXTENSIONS_ALLOWED || /*lint !e820*/(handle_case = (cli_or_cx_enabled && is_overloadable_handle_type(operand->type))) #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ )) { /* The "called function" is a class object. Look for operator() and surrogate functions, or for operator[] in the multi-subscript case. */ a_symbol_ptr member_function_symbol; a_type_ptr class_type = handle_case ? type_pointed_to(operand->type) : operand->type; class_type = skip_typerefs(class_type); check_assertion(!operand->bound_function); if (class_type->variant.class_struct_union.is_nonreal_class && !(type_is_lambda_closure(class_type) && lambda_closure_has_nondependent_call_type(class_type))) { /* A call of an object of a nonreal class type in a prototype instantiation cannot be resolved. (Closure types in template- dependent contexts are marked as nonreal because their definitions may depend on template parameters, but we can resolve their member functions and we want to be able to evaluate some cases like "[]{ return 1; }()" at compile time even when it occurs in a template-dependent context.) */ routine_type = NULL; prep_generic_operand(operand); unknown_dependent_function = TRUE; } else { /* If the class is a template class make sure it is instantiated so its operator() functions are visible. */ instantiate_template_class(class_type); try_surrogate_functions = !is_multi_subscript; overloaded_function_case = TRUE; /* routine_type = NULL; -- already set. */ /* The operand becomes the selector object. */ check_assertion(!operand->bound_function); copy_operand(operand, bound_function_selector); if (handle_case) { do_operand_transformations(bound_function_selector, TOPT_NO_OPTIONS); bound_function_selector->selector_is_object_pointer = TRUE; } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && is_delegate_type(class_type)) { /* A C++/CLI delegate class object can be invoked. Its Invoke function is called. */ a_routine_ptr invoke_rout = delegate_invocation_function(class_type); if (invoke_rout == NULL) { /* No Invoke member may be available in some template-dependent contexts. In such cases, proceed as if calling an unknown dependent function. */ member_function_symbol = NULL; routine_type = NULL; prep_generic_operand(operand); unknown_dependent_function = TRUE; overloaded_function_case = FALSE; } else { member_function_symbol = symbol_for(invoke_rout); } /* if */ implicit_delegate_invocation = TRUE; /* A delegate doesn't have surrogate functions, so don't bother looking for them. */ try_surrogate_functions = FALSE; } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here. */ { /* See if the class has an operator()/operator[]. */ member_function_symbol = opname_member_function_symbol( is_multi_subscript ? onk_subscript : onk_function_call, class_type); } /* if */ if (member_function_symbol != NULL) { /* There is an operator() function. The operand has become the selector object, and the function call operator routine becomes the operand. */ has_overloaded_call_operator = TRUE; /* We can use an indefinite function operand whether the operator() or operator[] function is overloaded or not. */ make_indefinite_function_operand(member_function_symbol, (a_symbol_locator*)NULL, operand); overloaded_function_symbol = member_function_symbol; bind_member_function_operand_to_selector( bound_function_selector, /*selector_is_object_pointer=*/handle_case, operand); } /* if */ } /* if */ } else { /* Neither a pseudo-destructor call, nor a function object call. */ /* If the operand is the name of a nonstatic member function (e.g., "A::f") convert it to a bound member function (e.g., "this->A::f"). This is done late so that A::f can be converted to a pointer-to-member implicitly in other contexts (that's an extension). */ if (is_sym_for_member_operand(operand) && is_a_function_designator(operand) && !operand->bound_function) { a_symbol_ptr member_func_sym = operand->symbol; a_type_ptr this_class = sym_parent_class(member_func_sym); if (((gpp_mode && !clang_mode) || microsoft_mode) && expr_stack != NULL && expr_stack->is_default_arg_expression && scope_stack_top().in_prototype_instantiation) { /* GCC does not appear to bind the member function to "this" in the prototype instantiation of a default argument. Instead, it treats it as an unknown function. */ turn_mem_func_operand_into_unknown_function(operand); } else { if (make_this_pointer_operand(member_func_sym, member_func_sym, &call_position, (a_boolean)operand-> access_control_error_reported, bound_function_selector)) { #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position saved_end_position; saved_end_position = operand->end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ if (this_class ->variant.class_struct_union.is_prototype_instantiation) { member_of_proto_inst = TRUE; } /* if */ /* Make an operand for the function bound to the "this" pointer. */ make_function_designator_operand( member_func_sym, (a_boolean)operand->is_qualified_name, /*compiler_generated=*/FALSE, &call_position, end_position_or_null(&saved_end_position), operand->ref_entries_list, operand); if (rcblock == NULL && !operand->name_reference_set && locator_for_curr_id.is_qualified_name && record_name_references_in_context()) { /* Remember the form of the name reference (it was set in scan_field_selection_operator for cases in which the object expression is explicit). */ set_operand_name_reference_from_locator(operand, &locator_for_curr_id); } /* if */ /* Note that the function designator will be converted to a pointer by the do_operand_transformations call just below. */ } else { /* There was some problem in constructing the "this" operand. */ conv_to_error_operand(operand); } /* if */ bind_member_function_operand_to_selector( bound_function_selector, /*selector_is_object_pointer=*/TRUE, operand); } /* if */ } /* if */ /* Do standard transformations on the operand. */ { a_transformation_options_set options = TOPT_WILL_CALL | TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION | TOPT_SUPPRESS_CHECK_FOR_INDEFINITE_FUNCTION; do_operand_transformations(operand, options); } if (is_undefined_symbol_operand(operand)) { /* The function designator is an undefined symbol. */ a_symbol_ptr func_sym = operand->symbol; #if EXTRA_SOURCE_POSITIONS_IN_IL /* Save the end position for later restoration. */ a_source_position end_function_position; end_function_position = operand->end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* In C++, it's an error, but not yet if argument-dependent lookup will be done -- in that case, a function might be found in an argument-dependent class or namespace, and no error is issued. */ if (!C_mode() && do_arg_dep_lookup) { overloaded_function_case = TRUE; overloaded_function_symbol = func_sym; /* routine_type = NULL; -- already set. */ } else { /* On a rescan, the symbol should not still be an sk_undefined if argument-dependent lookup was not done; it should have gone through the code below and should now be an sk_routine. */ check_assertion(rcblock == NULL); /* Implicitly declare the symbol as a function. */ enter_undefined_symbol(func_sym); decl_default_function(func_sym); /* In C++ mode and in strict C99 mode, issue an error (C++ and C99 do not allow implicitly declared functions, but most implementations of C99 appear to only issue a warning). In nonstrict C99 mode, a warning is issued. In all other C modes, a remark is issued. */ if (C_dialect == C_dialect_cplusplus || (c99_mode && strict_ansi_mode)) { if (expr_error_should_be_issued()) { pos_st_error(ec_undefined_identifier, &operand->position, func_sym->header->identifier); } /* if */ } else { an_error_severity sev = c99_mode ? es_warning : es_remark; expr_pos_st_diagnostic(sev, ec_implicit_func_decl, &operand->position, func_sym->header->identifier); } /* if */ make_function_designator_operand(func_sym, /*is_qualified_name=*/FALSE, /*compiler_generated=*/FALSE, &func_sym->decl_position, end_position_or_null( &end_function_position), operand->ref_entries_list, operand); if (!is_error_operand(operand)) { conv_function_designator_to_ptr_to_function( operand, (a_source_position *)NULL, /*allow_ctor=*/FALSE, /*will_call=*/TRUE); } /* if */ routine = func_sym->variant.routine.ptr; routine_type = routine_symbol_type(func_sym); } /* if */ } else if (is_indefinite_function_operand(operand)) { /* Overloaded function. That means the routine type is not known yet. */ overloaded_function_case = TRUE; overloaded_function_symbol = operand->symbol; /* routine_type = NULL; -- already set. */ if (overloaded_function_symbol->is_class_member) { if (symbol_is(overloaded_function_symbol, sk_function_template) && is_template_dependent_context()) { a_routine_ptr rp = overloaded_function_symbol ->variant.template_info ->variant.function.routine; if (special_kind_is(rp, sfk_conversion)) { /* This is an explicit call to a conversion function template in a dependent context, i.e., something like: return operator T(); where T is a template parameter. Such a case cannot be handled by the normal deduction machinery and is therefore handled as an unknown dependent function instead. */ prep_generic_operand(operand); unknown_dependent_function = TRUE; overloaded_function_case = FALSE; } /* if */ } /* if */ if (rcblock != NULL && !unknown_dependent_function && overloaded_function_symbol->overload_set_member) { /* We're rescanning a call to a member, and earlier found a single candidate. However, in the completed class, additional candidates may have been added. Redo the lookup to pick up those candidates. The language actually says that if this new lookup yields a different result, the program is ill-formed, but no diagnostic is required. However, redoing the lookup and not issuing a diagnostic is existing practice. For example: template struct B { static constexpr bool value = b; }; struct S { template static bool test(); template static auto test(int) -> B()>; template static B test(...); }; auto r = S::test(0); Here, the call "test()" finds only the first declaration of S::test, but when instantiated common practice is to find all the declarations. The added instantiations make the rescanned call ambiguous, which takes S::test(int) out of the candidate set and therefore S::test(...) is unambiguously selected. */ a_symbol_locator locator; a_type_ptr class_type = sym_parent_class(overloaded_function_symbol); make_locator_for_symbol(overloaded_function_symbol, &locator); clear_specific_symbol(locator); overloaded_function_symbol = class_qualified_id_lookup(&locator, class_type, IDL_NO_OPTIONS); check_assertion(overloaded_function_symbol != NULL && symbol_is(overloaded_function_symbol, sk_overloaded_function)); operand->symbol = overloaded_function_symbol; } /* if */ } /* if */ } else if (!C_mode() && is_template_dependent_context() && (is_template_param_type_or_ref_thereto(operand->type) || ((gpp_mode || clang_mode || microsoft_mode) && is_template_dependent_type(operand->type) && !stricter_template_checking))) { /* A call of a dependent expression in a prototype instantiation. Note that we test only for a top-level parameter type here, which might be a class. If the call target has a dependent type (which could be due to the implicit object parameter of a non-static member function of a prototype instantiation), Clang, GCC, and MSVC consider it template-dependent, too. More testing for other dependent cases is done below. */ routine_type = NULL; prep_generic_operand(operand); if ((gpp_mode || clang_mode || microsoft_mode) && member_of_proto_inst && !stricter_template_checking) { /* Make the call target opaque. */ make_template_param_expr_constant_operand(operand); operand->type = type_of_unknown_templ_param_nontype; operand->variant.constant.type = type_of_unknown_templ_param_nontype; operand->bound_function = FALSE; } /* if */ unknown_dependent_function = TRUE; } else { /* Normal function, or call using pointer-to-member-function. */ /* Convert to prvalue. This conversion is needed particularly for the case int (*fp)(); fp(); I.e., a call using a pointer to function, with no explicit "*". */ conv_glvalue_to_prvalue(operand); /* Expression must be of type "pointer to function" or "pointer to member function (bound)". */ if (operand->bound_function && is_ptr_to_member_type(operand->type)) { /* Call of a bound function pointer, as in (p->*pmf)(1, 2); */ routine_type = pm_member_type(operand->type); } else if (!C_mode() && is_template_dependent_context() && is_pointer_or_handle_type(operand->type) && is_template_param_type(type_pointed_to(operand->type))) { /* A pointer to a template parameter type, which might be a function type. */ routine_type = NULL; routine = NULL; unknown_dependent_function = TRUE; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (!C_mode() && microsoft_mode && (op_is_zero_constant(operand) || (is_void_type(operand->type) && is_expression_operand(operand) && (castexp = skip_parens(operand->variant.expression), is_operation_node(castexp)) && node_operator_is(castexp, eok_cast) && (castexp = skip_parens(castexp->variant.operation.operands), is_constant_node(castexp)) && is_zero_constant(node_constant(castexp))))) { /* Microsoft Visual C++ allows a call like 0(x) -- it is ignored. */ /* (void)0 is also allowed. */ expr_pos_warning(ec_call_of_zero, &operand->position); routine_type = NULL; routine = NULL; ignore_call = TRUE; saved_evaluated = expr_stack->evaluated; saved_potentially_evaluated = expr_stack->potentially_evaluated; expr_stack->evaluated = FALSE; expr_stack->potentially_evaluated = FALSE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else if (check_call_function_pointer_operand(operand)) { #if OPTIMIZE_VIRTUAL_FUNCTION_CALLS orig_routine_type = operand->orig_routine_type; #endif /* OPTIMIZE_VIRTUAL_FUNCTION_CALLS */ routine_type = type_pointed_to(operand->type); routine = routine_from_function_operand(operand); if (!operand->is_id_expression && is_constant_operand(operand)) { /* Constant-folding can sometimes recover the actual callee from a what looks like an indirect call. For example, in C++23 mode: struct S { void f(this S const&); void g() { constexpr auto cfp = &S::f; f(); cfp(); // Should be an error, unlike "f()". } }; Constant-folding recovers the callee for the call cfp(), but that doesn't mean it should be treated like a direct call. */ routine = NULL; } /* if */ } /* if */ if (!C_mode() && is_template_dependent_context() && routine == NULL && routine_type != NULL && is_template_dependent_type(routine_type)) { /* Call through a template-dependent pointer to function or pointer to member function. Suppress argument checking. */ routine_type = NULL; unknown_dependent_function = TRUE; } /* if */ } /* if */ if (C_mode() || routine == NULL) { /* Nothing to do. */ } else if (routine->trailing_requires_clause != NULL || (routine_type != NULL && skip_typerefs(routine_type)->variant.routine.extra_info ->has_enable_if_attribute)) { /* Force the use of overload resolution if constraints have to be checked. This is true both for C++20 trailing requires clauses and for enable_if attributes. */ overloaded_function_case = TRUE; overloaded_function_symbol = symbol_for(routine); } else if (routine_type != NULL && has_explicit_this_parameter(skip_typerefs(routine_type))) { overloaded_function_case = TRUE; overloaded_function_symbol = symbol_for(routine); } /* if */ /* Change the kind in the reference entry for the function from an address-taken entry back to a simple reference. */ change_some_ref_kinds(operand->ref_entries_list, SRK_ADDRESS_TAKEN, SRK_REFERENCE); } /* if */ #if BUILTIN_FUNCTIONS_ENABLED operand_routine = routine_from_function_operand(operand); if (operand_routine != NULL && is_gnu_builtin_function(operand_routine)) { /* See if this is a call to a predeclared builtin function that needs some type of adjustment. The vast majority of builtin functions need no adjustment, but some compiler "magic" is needed in other cases. In many of these cases the builtin's signature (often "void(...)") is meaningless. If adjustment is needed, the concrete routine to call will not be known until after the arguments are scanned. */ if (builtin_call_needs_adjustment(operand_routine, &bca)) { bcap = &bca; routine_type = NULL; routine = NULL; unknown_dependent_function = FALSE; if (bcap->overloaded_function_symbol != NULL) { /* The builtin is being replaced by a call to a global overloaded operator function; use that symbol to do the overload resolution. */ overloaded_function_symbol = bcap->overloaded_function_symbol; overloaded_function_case = TRUE; operand->is_qualified_name = TRUE; do_arg_dep_lookup = FALSE; } /* if */ } /* if */ } /* if */ #endif /* BUILTIN_FUNCTIONS_ENABLED */ if (expr_stack->any_suppressed_error && rcblock != NULL) { /* If there were suppressed errors, the call is unreliable and further substitutions are not helpful. */ make_error_operand(result); goto done; } /* if */ if (orig_routine_type == NULL) { /* If this call is the result of optimizing a virtual function call to a direct call of an overriding function, we must use the type of the overridden function for scanning the arguments in order to pick up the correct default arguments, if any. In all other cases, the original routine type will be NULL and we just use the regular routine type. */ orig_routine_type = routine_type; } /* if */ /* Scan the arguments of the call. For non-overloaded function calls, this checks the arguments against the parameter types and converts them as necessary. */ { /* Enter a new scope to avoid issues with preceding goto statements. */ a_boolean builtin_needs_adjustment = FALSE; a_boolean return_raw_arguments, generic_array = FALSE; #if BUILTIN_FUNCTIONS_ENABLED builtin_needs_adjustment = bcap != NULL && (bcap->callback != NULL || bcap->is_invoke); #endif /* BUILTIN_FUNCTIONS_ENABLED */ if (is_multi_subscript) { overloaded_function_case = TRUE; generic_array = unknown_dependent_function; unknown_dependent_function = FALSE; } /* if */ return_raw_arguments = overloaded_function_case || builtin_needs_adjustment; scan_call_arguments(orig_routine_type, routine, is_multi_subscript ? tok_rbracket : tok_rparen, already_after_left_paren, &argument_list, return_raw_arguments, &unknown_dependent_function, /*args_will_be_discarded=*/is_error_operand(operand), /*is_custom_ms_attr_arg_list=*/FALSE, rcblock, /*arg_list_supplied=*/FALSE, (an_arg_list_elem *)NULL, &arg_list, (an_operand *)NULL, (a_boolean *)NULL, &closing_paren_position); if (unknown_dependent_function && overloaded_function_symbol == NULL && routine != NULL) { /* In some modes scanning a dependent call argument list during a prototype instantiation will set unknown_dependent_function to TRUE. Treat such cases using overload resolution as if the called function were dependent. */ overloaded_function_case = TRUE; overloaded_function_symbol = symbol_for(routine); } /* if */ if (is_multi_subscript) { if (p_subscript != NULL && is_single_elem(arg_list) && !generic_array && is_expression_component(arg_list)) { /* This is a single subscript: Have it handled by the normal subscript processing. */ an_operand *opnd = operand_of_arg_list_elem(arg_list); copy_operand(opnd, p_subscript); /* Any structures owned by the operand are transferred to *p_subscript and should not be freed by this function. */ opnd->ref_entries_list = NULL; goto done; } else { a_source_position end_pos = pos_curr_token; if (rcblock == NULL) { (void)required_token(tok_rbracket, ec_exp_rbracket); } /* if */ if (generic_array || unknown_dependent_function) { template_multi_subscript_operation(eok_subscript, operand, arg_list, result, &operator_position, opening_paren_tok_seq_number, &end_pos); set_operand_position(result, &operand->position, &end_pos, &operator_position); goto done; } /*if */ } /* if */ } /* if */ } if (rcblock != NULL && (expr_stack->any_suppressed_error || rcblock->error_detected)) { /* If there were suppressed errors, the call is unreliable and further substitutions are not helpful. */ make_error_operand(result); goto done; } /* if */ error_position = call_position; #if BUILTIN_FUNCTIONS_ENABLED if (bcap != NULL && adjust_builtin_call_arguments(bcap, operand_routine, bound_function_selector, &call_position, &closing_paren_position, &operand, &arg_list, &argument_list, &routine_type, &routine, &overloaded_function_case, &overloaded_function_symbol, &unknown_dependent_function, &try_surrogate_functions, &has_overloaded_call_operator, &delay_free_arg_list_elem, result)) { /* The builtin was fully handled (e.g., the pointer-to-data-member form of __builtin_invoke), or an error was detected. */ goto done; } /* if */ #endif /* BUILTIN_FUNCTIONS_ENABLED */ if (overloaded_function_case) { an_operand orig_operand; a_boolean name_reference_was_saved = FALSE; a_name_reference saved_name_reference = null_name_reference; a_template_arg_ptr template_arg_list; if (is_multi_subscript) { ovl_context = oc_multi_subscript; } else if (operand->is_microsoft_deferred_name) { ovl_context = oc_deferred; } else if (cpp23_mode && operand->is_operand_of_address_of && is_indefinite_function_operand(operand)) { /* Check whether this is a call of the form (&func)(args...) where func is an overload set. This affects overload resolution, and is also used below to mark the resulting call node so that the C++-generating back end can emit the call in the same form. */ ovl_context = oc_call_through_address_of_overload_set; } /* if */ template_arg_list = operand->template_arg_list; orig_operand = *operand; if (operand->name_reference_set) { /* We have recorded the form of reference of the function name. Save it for incorporation in the call expression later. */ saved_name_reference = operand->name_reference; name_reference_was_saved = TRUE; } /* if */ /* Choose the proper function out of a set of overloaded functions based on the argument types. */ if (!select_and_prepare_to_call_overloaded_function( overloaded_function_symbol, (a_boolean)operand->is_template_id, &template_arg_list, operand->bound_function || try_surrogate_functions, bound_function_selector, &arg_list, do_arg_dep_lookup, /*use_pure_arg_dep_lookup=*/FALSE, /*use_std_for_arg_dep_lookup=*/FALSE, try_surrogate_functions, /*is_property=*/FALSE, /*compiler_generated=*/FALSE, ovl_context, &orig_operand, &call_position, opening_paren_tok_seq_number, &closing_paren_position, &found_through_adl, operand, &argument_list)) { /* Some error, e.g., none of the overloaded functions matches the argument list. */ make_error_operand(operand); free_template_arg_list(template_arg_list); } else { /* Overload resolution was successful (including the case where the function is unknown because the arguments are dependent). */ if (name_reference_was_saved) { /* Restore information on the form of the name reference for the function name. */ operand->name_reference = saved_name_reference; operand->name_reference_set = TRUE; if (operand->name_reference.is_template_id) { /* Now that a specific function has been selected, any non-type template arguments have been converted to the corresponding parameter types. Update the recorded template argument list. */ a_template_arg_ptr argp, argp_tail = NULL; begin_template_arg_list_traversal_simple(template_arg_list, &argp); for (; argp != NULL && argp->explicitly_specified; advance_to_next_template_arg_simple(&argp)) { argp_tail = argp; } /* for */ /* Remove any trailing default template arguments. */ free_template_arg_list(argp); if (argp_tail == NULL) { template_arg_list = NULL; } else { argp_tail->next = NULL; } /* if */ operand->name_reference.orig_template_arg_list = template_arg_list; } /* if */ } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (implicit_delegate_invocation) { /* In an implicit delegate invocation, the Invoke function reference is implicit. */ change_ref_kinds(operand->ref_entries_list, (SRK_REFERENCE | SRK_IMPLICIT)); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ } else if (vacuous_destructor_case) { /* A vacuous destructor call. The argument list should have no arguments. */ if (argument_list != NULL) { expr_pos_error(ec_too_many_arguments, &first_arg_position); conv_to_error_operand(operand); } /* if */ } else { /* Non-overloaded function case. */ if (operand->bound_function && routine_type != NULL) { /* Non-static member function call. */ /* Check that the selector object is compatible with the "this" parameter type. It isn't, for example, if we are calling a non-const-qualified function with a const-qualified object. Note that if a base class cast was required, it has already been done during the function binding, so the differences at this point (other than for error cases) are const/non-const differences. */ a_type_ptr this_param_type = object_parameter_type( routine_type, (a_symbol *)NULL, /*is_conv_func=*/FALSE); selector_match_with_this_param(bound_function_selector, routine, routine_type, this_param_type, &this_match_summary); if (this_match_summary.match_level != aml_none) { /* The types are compatible. */ /* Issue any needed warning (e.g., cfront anachronism of calling a non-const function with a const selector). */ issue_warning_from_arg_match_summary(&this_match_summary, &bound_function_selector-> position); change_refs_on_selector(routine_type, bound_function_selector); } else if (microsoft_bugs && microsoft_version <= 1300 && routine == NULL && is_ptr_to_member_type(operand->type)) { /* MSVC++ 6.0 and 7.0 allow a call via a pointer to member where the object is constant and the function is not, e.g., (const_obj_ptr->*pm_non_const_func)(); */ expr_pos_warning(ec_unqual_function_with_qual_object, &bound_function_selector->position); } else if (expr_error_should_be_issued()) { /* Some mismatch (either a value category mismatch, or more qualifiers on the selector than on the "this" parameter type). */ report_this_param_mismatch(routine, this_param_type, bound_function_selector, &this_match_summary); conv_to_error_operand(bound_function_selector); } /* if */ } /* if */ } /* if */ if (vacuous_destructor_case) { /* Vacuous destructor case; leave the original operand alone. */ copy_operand(operand, result); #if MICROSOFT_EXTENSIONS_ALLOWED } else if (ignore_call) { /* Ignore a call of the form 0(x) -- copy the zero to the result. */ copy_operand(operand, result); result->is_microsoft_noop = TRUE; expr_stack->evaluated = saved_evaluated; expr_stack->potentially_evaluated = saved_potentially_evaluated; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else { /* Build the call node and an operand for it. This includes cases where the function is unknown because the call is dependent. */ a_boolean uses_operator_syntax = FALSE; routine = routine_from_function_operand(operand); if (has_overloaded_call_operator) { if (routine != NULL && special_kind_is(routine, sfk_operator) && routine->variant.opname_kind == (an_opname_kind)onk_function_call) { /* The original "function" was a class object with an operator() or operator[] member, and the resulting call is such an operator (as opposed to using a conversion operator to a function pointer): mark the call as using operator syntax so the C++-generating back end can reconstruct the original source form (e.g., "x()" instead of "x.operator()()"). */ uses_operator_syntax = TRUE; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (implicit_delegate_invocation) { /* An implicit invocation of a C++/CLI delegate has the same issue. */ uses_operator_syntax = TRUE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ } /* if */ an_expr_node_ptr function_call_node = NULL; assemble_function_call(operand, bound_function_selector, argument_list, /*compiler_generated=*/FALSE, arg_dep_lookup_suppressed, adl_suppressed_by_qualification, found_through_adl, uses_operator_syntax, &start_position, &operator_position, &closing_paren_position, result, &call_folded_to_constant, &function_call_node); if (function_call_node != NULL) { if (is_multi_subscript) { function_call_node->variant.operation.call_uses_operator_syntax = TRUE; } /* if */ #if BACK_END_IS_CP_GEN_BE if (ovl_context == oc_call_through_address_of_overload_set) { /* Record that this call was of the form "(&f)(args)" so the C++-generating back end can reproduce that form. */ function_call_node->variant.operation. called_through_address_of_overload_set = TRUE; } /* if */ #endif /* BACK_END_IS_CP_GEN_BE */ record_operator_position_in_expr_rescan_info(function_call_node, &operator_position, opening_paren_tok_seq_number, &closing_paren_position); } /* if */ } /* if */ if (saved_uses_this_operand && !expr_stack->uses_this_operand) { /* If the surrounding expression used "this", restore that information for a potential enclosing call. */ expr_stack->uses_this_operand = TRUE; } /* if */ set_operand_position(result, &start_position, &closing_paren_position, &operator_position); #if BUILTIN_FUNCTIONS_ENABLED if (!call_folded_to_constant) #endif /* BUILTIN_FUNCTIONS_ENABLED */ { /* A function call rules out a constant expression. */ rule_out_expr_kinds(ROEK_CONSTANT, result); } /* if */ #if BUILTIN_FUNCTIONS_ENABLED if (bcap != NULL) { if (bcap->name_reference != NULL && !is_error_operand(result)) { /* The builtin call has been replaced by a call to the appropriate operator new/delete; use a name reference to communicate this to the back end so the original source can be recreated. */ an_expr_node_ptr operand_node; check_assertion(result->kind == ok_expression && node_is_operator(result->variant.expression, eok_call)); operand_node = result->variant.expression->variant.operation.operands; switch (operand_node->kind) { case enk_routine: operand_node->variant.routine.name_reference = bcap->name_reference; break; case enk_constant: operand_node->variant.constant.name_reference = bcap->name_reference; break; default: unexpected_condition_str("scan_function_call: bad operand kind"); } /* switch */ } /* if */ if (!unknown_dependent_function && bcap->result_type != NULL) { /* Cast the call result to the right type for certain builtin function calls. */ cast_operand(bcap->result_type, result, /*is_implicit_cast=*/TRUE); } /* if */ } /* if */ if (delay_free_arg_list_elem != NULL) { free_arg_list_elem(delay_free_arg_list_elem); } /* if */ #endif /* BUILTIN_FUNCTIONS_ENABLED */ done: free_arg_list(arg_list); expr_stack->expression_kind = saved_expr_kind; db_exit(); } /* scan_function_call */ static a_symbol_ptr other_field_with_same_name(void) /* In pcc and SVR4 modes, a field selection can refer to a field that is not in the struct or union indicated by the left operand. This routine finds the symbol entry for any field of the same name as the current identifier. If there is none, or if there is more than one and they do not all have the same offset, an error is issued and NULL is returned. */ { a_symbol_ptr other_field_sym, temp_field_sym; other_field_sym = NULL; for (temp_field_sym = locator_for_curr_id.symbol_header->inactive_symbols; temp_field_sym != NULL; temp_field_sym = temp_field_sym->next) { if (temp_field_sym->kind == (a_symbol_kind)sk_field) { if (other_field_sym == NULL) { /* First field found. Just save. */ other_field_sym = temp_field_sym; } else { /* Field after the first. All the offsets must match. */ if ((other_field_sym->variant.field.ptr->offset != temp_field_sym->variant.field.ptr->offset) || (other_field_sym->variant.field.ptr->offset_bit_remainder != temp_field_sym->variant.field.ptr->offset_bit_remainder)) { /* Mismatch, so the field reference cannot be unambiguously resolved. */ if (expr_error_should_be_issued()) { pos_sy2_error(ec_pcc_field_ambiguity, &pos_curr_token, other_field_sym, temp_field_sym); } /* if */ other_field_sym = NULL; break; } /* if */ } /* if */ } /* if */ } /* for */ if (temp_field_sym == NULL && other_field_sym == NULL) { /* No field was found. */ if (expr_error_should_be_issued()) { str_error(ec_not_a_field_name, locator_for_curr_id.symbol_header->identifier); } /* if */ } /* if */ return other_field_sym; } /* other_field_with_same_name */ static void make_field_selection_operand( an_operand *operand_1, an_expr_operator_kind op, a_symbol_locator *field_locator, a_source_position *member_position, a_source_position *end_position, a_type_ptr selection_type, a_boolean is_lvalue, a_boolean compiler_generated, an_operand *result) /* Make an operand for a field selection. *operand_1 is the left operand. op is the selection operator. field_locator describes the right operand (the field). selection_type is the result type. The result is an lvalue if is_lvalue is TRUE, a prvalue otherwise (note that for xvalue cases the node will be built as an lvalue here and then the caller will change it to an xvalue). The field selection is compiler-generated if compiler_generated is TRUE. The operand for the selection is created in *result. member_position and end_position give the starting and ending source positions for the field reference (end_position only in configurations with extra source positions). */ { a_symbol_ptr field_sym = field_locator->specific_symbol; an_operand field_operand; reduce_projection_symbol_to_fundamental_symbol(field_sym); check_assertion(field_sym->kind == (a_symbol_kind)sk_field); make_field_operand(field_locator, member_position, end_position, &field_operand); build_binary_result_operand_full(operand_1, &field_operand, op, selection_type, is_lvalue, result); check_assertion(is_expression_operand(result) && is_operation_node(result->variant.expression)); if (compiler_generated) { result->variant.expression->compiler_generated = TRUE; if (result->variant.expression->position.seq == 0) { /* If the node has no explicit position, use the enk_field operand node instead. */ result->variant.expression->position = operand_1->position; } /* if */ } /* if */ /* When nonstandard anonymous unions are allowed (including C11 anonymous unions and anonymous structs), look for fields of the anonymous parents and insert the elided field selections. */ if (field_sym->variant.field.anonymous_parent_object != NULL) { an_expr_node_ptr orig_node = result->variant.expression; adjust_nonstandard_anonymous_object_field_references(orig_node, field_sym, /*std_also=*/FALSE); make_lvalue_or_rvalue_expression_operand(orig_node, result); } /* if */ if (!is_lvalue && constexpr_enabled && !expr_stack->consteval_call_need_not_fold) { /* See if the field selection folds to a constant (usually this happens on the glvalue-to-prvalue conversion, but in this case we're building a prvalue immediately). */ a_constant_ptr constant = local_constant(); a_boolean release_constant = TRUE; check_assertion(is_expression_operand(result) && is_a_prvalue(result)); if (fold_constexpr_member_selection(result->variant.expression, constant)) { an_operand orig_operand; copy_operand(result, &orig_operand); if (constant_is(constant, ck_aggregate)) { /* Create a temporary to hold the constant result. */ 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, &result->position, &temp_dip); set_dynamic_init_constant(temp_dip, move_local_constant_to_il(&constant)); release_constant = FALSE; make_expression_operand(temp_node, result); } else { make_constant_operand(constant, result); } /* if */ restore_operand_details(result, &orig_operand); /* Preserve the original expression that was folded. */ result->variant.constant.expr = orig_operand.variant.expression; } /* if */ if (release_constant) release_local_constant(&constant); } /* if */ } /* make_field_selection_operand */ static void do_field_selection_operation( an_operand *operand_1, a_type_ptr class_struct_union_type, a_boolean is_arrow_operator, a_boolean compiler_generated, a_symbol_locator *field_locator, a_source_position *member_position, a_source_position *end_position, a_ref_entry_ptr rep, an_operand *result) /* Construct the result operand for a field selection operation. The left operand (the class/struct/union or the pointer thereto) is given by operand_1. The type of the class/struct/union (before C++ baseward casts, if any) is given by class_struct_union_type; it provides the type qualifiers that should be attached to the result expression. The operator is "->" if is_arrow_operator is TRUE, "." otherwise. compiler_generated is TRUE if this selection is compiler-generated (e.g., an implicit "this->" on a nonstatic data member reference). field_locator identifies the symbol for the right-side field. rep points to an associated reference entry, or is NULL if none is needed. The result is placed in *result. member_position and end_position give the starting and ending source positions for the field reference (end_position only in configurations with extra source positions). This routine also accepts the case where the first operand is a C++/CLI handle. */ { a_symbol_ptr field_sym = field_locator->specific_symbol; a_field_ptr field; a_type_ptr result_type; a_type_ptr selection_type; an_expr_operator_kind op; a_type_qualifier_set qualifiers; a_boolean result_is_a_glvalue = FALSE; a_boolean result_is_an_xvalue = FALSE; reduce_projection_symbol_to_fundamental_symbol(field_sym); check_assertion(field_sym->kind == (a_symbol_kind)sk_field); field = field_sym->variant.field.ptr; if (is_error_operand(operand_1)) { make_error_operand(result); #if MICROSOFT_EXTENSIONS_ALLOWED } else if (ms_extensions && field_is_property_or_event(field)) { if (property_or_event_kind_is(field, pek_declspec_property)) { /* A property field in Microsoft C++ mode. Render as an ok_property_ref operand, which will be rewritten later as a function call. */ make_property_ref_operand(field_sym, operand_1, is_arrow_operator, result); result->id_position = field_locator->source_position; } else if (property_or_event_kind_is(field, pek_cli_event)) { /* An event field in C++/CLI mode. Render as an ok_event_ref operand: It will be rewritten later. */ make_event_ref_operand(field_sym, operand_1, is_arrow_operator, result); } else { /* Note that, in particular, C++/CLI properties do not get here because they're handled via sk_property_set symbols rather than sk_field symbols. */ unexpected_condition(); } /* if */ set_operand_id_details_from_locator(result, field_locator); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else { if (c11_atomic_enabled && class_struct_union_type != NULL && is_c11_atomic_qualified_type(class_struct_union_type)) { /* C11 makes accessing a member of an atomic object undefined behavior. We just issue a warning in such cases (this matches, e.g., GCC's behavior). */ pos_warning(ec_access_to_member_of_c11_atomic_object, &operand_1->position); } /* if */ /* Determine the value category of the result. */ if (is_arrow_operator) { /* "->" always produces an lvalue result. */ result_is_a_glvalue = TRUE; } else if (is_an_lvalue(operand_1)) { /* lvalue.field produces an lvalue result. */ result_is_a_glvalue = TRUE; } else if (is_an_xvalue(operand_1) || (selection_from_prvalue_is_xvalue && !(microsoft_bugs && !is_class_struct_union_type(field->type)))) { /* xvalue.field produces an xvalue result. In C++14, the same is true for prvalue.field (through the resolution of Core issue 616). MSVC doesn't seem to apply that rule if the field is not of class type. */ result_is_a_glvalue = TRUE; result_is_an_xvalue = TRUE; } else { /* prvalue.field produces a prvalue result in C++11 and earlier. */ result_is_a_glvalue = FALSE; if (microsoft_bugs && microsoft_version < 1600 && is_floating_type(field->type)) { /* For some unknown reason, MSVC considers a selection of a field of a floating-point type out of a class prvalue to be an lvalue. Checked in 7.1, 8.0, 10.0 beta. Fixed in real 10.0 release. */ revert_microsoft_rvalue_to_lvalue_if_possible(operand_1); result_is_a_glvalue = is_an_lvalue(operand_1); } /* if */ } /* if */ /* Determine the result type. */ qualifiers = get_type_qualifiers(class_struct_union_type); if ((!result_is_a_glvalue || result_is_an_xvalue) && is_template_param_type(operand_1->type)) { /* If the left operand is of template parameter type, the value category might not be accurate, so we need to consider the result to be dependent. */ result_type = type_of_unknown_templ_param_nontype; } else if (cfront_2_1_mode) { /* cfront 2.1 ignores the cv-qualifiers on the left operand. */ result_type = field->type; if (qualifiers != TQ_NONE) { a_type_ptr unqual_class_type; a_boolean operand_1_was_rvalue = (is_an_rvalue(operand_1) && !is_arrow_operator); /* Drop the type qualifiers on the left operand to generate correct IL. */ /* Adjust the type by turning the operand into a pointer (if necessary) and casting. */ conv_selector_to_object_pointer(operand_1, &is_arrow_operator); /* Note that we cannot simply use the unqualified version of class_struct_union_type here because it might be a derived class. */ unqual_class_type = type_pointed_to(operand_1->type); unqual_class_type = make_unqualified_type(unqual_class_type); cast_operand(make_pointer_type(unqual_class_type), operand_1, /*is_implicit_cast=*/TRUE); if (operand_1_was_rvalue) { /* For the class rvalue case, produce an rvalue again. */ conv_object_pointer_to_lvalue(operand_1); conv_glvalue_to_prvalue(operand_1); is_arrow_operator = FALSE; } /* if */ qualifiers = TQ_NONE; } /* if */ } else { /* The result type is set to the type of the field with the union of the qualifiers of the field and the qualifiers of the class, struct, or union. const is ignored if the field was declared mutable. */ result_type = make_field_selection_type(field, qualifiers); } /* if */ selection_type = result_type; if (!result_is_a_glvalue) result_type = prvalue_type(result_type); /* Determine the IL operator to use. */ op = is_arrow_operator ? (an_expr_operator_kind)eok_points_to_field : (an_expr_operator_kind)eok_dot_field; /* Construct the field selection expression tree. */ make_field_selection_operand(operand_1, op, field_locator, member_position, end_position, selection_type, result_is_a_glvalue, compiler_generated, result); /* In C++, a field may have a reference type. An implicit indirection is done to get the thing pointed to. */ if (!C_mode() && is_any_reference_type(result_type)) { add_reference_indirection(result); #if MICROSOFT_EXTENSIONS_ALLOWED } else if (is_tracking_reference_type(result_type)) { /* Fields can't have tracking reference type. */ unexpected_condition(); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else { /* Preserve the reference entries for the base struct. Don't do this if we are dereferencing a reference, because in that case the reference is not modified if the glvalue is modified. */ result->ref_entries_list = operand_1->ref_entries_list; if (rep != NULL) { /* Add the reference entry for the field to the list of entries for the operand. */ rep->next_operand_ref = result->ref_entries_list; result->ref_entries_list = rep; } /* if */ if (result_is_an_xvalue) { conv_rvalue_reference_result_to_xvalue(result); } /* if */ } /* if */ } /* if */ } /* do_field_selection_operation */ /* Data structure used by process_overloaded_operator_arrow to check for loops in operator-> conversions. */ typedef struct an_operator_arrow_block *an_operator_arrow_block_ptr; typedef struct an_operator_arrow_block { an_operator_arrow_block_ptr parent; /* The block for the previous iteration, or NULL if this is the first. */ a_type_ptr class_type; /* The class type in this iteration. */ } an_operator_arrow_block; #if MICROSOFT_EXTENSIONS_ALLOWED static a_boolean is_class_with_operator_arrow_for_cli( a_type_ptr class_type, an_operator_arrow_block_ptr parent) /* Return TRUE if class_type (a possibly cv-qualified class type) has an operator-> that can be used in C++/CLI. C++/CLI's handling of operator-> is complicated by the fact that a handle-to-class can be used both to call an operator-> function and to directly reference a member of the class. MSVC, when given such a handle, deals with the ambiguity by looking to see whether the class has an operator-> (or a chain of operator-> functions) that produces a handle or a pointer to class. If it does, the operator-> interpretation is used. parent points to a list of entries indicating classes already being processed, as a way to avoid loops. */ { a_boolean has_op_arrow = FALSE; a_type_ptr unqual_class_type = skip_typerefs(class_type); check_assertion(is_class_struct_union_type(unqual_class_type)); /* Don't process template-dependent classes in prototype instantiations. */ if (!unqual_class_type->variant.class_struct_union.is_nonreal_class) { /* Get the operator-> function of the class, if any. */ a_symbol_ptr sym = opname_member_function_symbol((an_opname_kind)onk_arrow, unqual_class_type); if (sym != NULL) { a_type_ptr return_type; sym = fundamental_symbol_of(sym); check_assertion(sym->kind == (a_symbol_kind)sk_member_function); return_type = return_type_of(sym->variant.routine.ptr->type); if (is_handle_type(return_type) || (is_pointer_type(return_type) && is_class_struct_union_type(type_pointed_to(return_type)))) { /* The operator-> function returns a handle or pointer-to-class, so it can be used. */ has_op_arrow = TRUE; } else if (is_class_struct_union_type(return_type)) { /* The operator-> function returns a class (or a reference to a class), so we need another operator-> function in that class. */ /* Check that we're not looping on a class type we've already encountered. Note that cv-qualifiers are not ignored, because they can make a difference in which operator-> function is selected. */ an_operator_arrow_block block; an_operator_arrow_block_ptr aobp; for (aobp = parent; aobp != NULL; aobp = aobp->parent) { if (identical_types(return_type, aobp->class_type)) { /* There's a loop. */ has_op_arrow = FALSE; goto end_of_routine; } /* if */ } /* for */ /* Add another entry to the list so we will give up if we get back to this new class. Then do a recursive call to see if this class can be converted to a handle or pointer. We don't fill out all the fields of the entry because we don't need them in this limited context. */ block.parent = parent; block.class_type = return_type; if (is_class_with_operator_arrow_for_cli(return_type, &block)) { has_op_arrow = TRUE; } /* if */ } else { /* Other return types, e.g., error. */ has_op_arrow = FALSE; } /* if */ } /* if */ } /* if */ end_of_routine: return has_op_arrow; } /* is_class_with_operator_arrow_for_cli */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ static void process_overloaded_operator_arrow( an_operand *operand, a_source_position *operator_position, a_token_sequence_number tsn, a_nondependent_call_depth depth, an_operator_arrow_block_ptr parent) /* operand is the first operand of a "->" field selection in C++. See whether an operator-> function (or several) applies to convert the operand to a class or pointer to class. If so, do the transformation and return the updated operand. operator_position is the position of, and tsn is the token sequence number of, the "->" token. depth indicates the recursion depth (1 for a top-level call). parent points to a list of blocks indicating transformations done so far on this operand, as a way to catch loops. */ { #if MICROSOFT_EXTENSIONS_ALLOWED a_boolean handle_case = FALSE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Note that we do not use "is_overloadable_type_operand" here. That's deliberate: doing so could cause infinite loops. */ if (is_class_struct_union_type(operand->type) #if MICROSOFT_EXTENSIONS_ALLOWED || /*lint !e820*/(handle_case = (cli_or_cx_enabled && is_overloadable_handle_type(operand->type) && is_class_with_operator_arrow_for_cli( type_pointed_to(operand->type), parent))) #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ ) { an_operand result; a_boolean processed = FALSE; a_type_ptr qual_class_type = operand->type; a_type_ptr class_type; an_operator_arrow_block_ptr aobp; #if MICROSOFT_EXTENSIONS_ALLOWED if (handle_case) qual_class_type = type_pointed_to(qual_class_type); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ class_type = skip_typerefs(qual_class_type); /* See whether the class type has been encountered previously. If so, we have a loop. Note that cv-qualifiers are not ignored, because they can make a difference in which operator-> function is selected. */ for (aobp = parent; aobp != NULL; aobp = aobp->parent) { if (identical_types(qual_class_type, aobp->class_type)) { /* Loop in operator-> return types. */ if (expr_error_should_be_issued()) { pos_ty_error(ec_op_arrow_loop, &operand->position, class_type); } /* if */ conv_to_error_operand(operand); goto end_of_routine; } /* if */ } /* for */ /* Don't process template-dependent classes in prototype instantiations. */ if (!class_type->variant.class_struct_union.is_nonreal_class) { /* Check for an overloaded operator->. */ /* The operator is treated as a unary operator (i.e., the field following the "->" is not significant at this point). */ check_for_operator_overloading((an_opname_kind)onk_arrow, /*unary_operator=*/TRUE, /* sic */ /*must_be_member_function=*/TRUE, /*try_conversions=*/FALSE, /*has_predef_meaning=*/TRUE, operand, (an_operand *)NULL, operator_position, tsn, depth, (a_source_position *)NULL, &result, &processed); } /* if */ if (processed) { an_operator_arrow_block block; /* An operator-> function was found and applied. */ set_operand_position(&result, &operand->position, &operand->end_position, operator_position); copy_operand(&result, operand); /* If the operator function returns a class object or reference to class object, look for another operator->() function. Maintain a list of classes already encountered to allow checking for loops. */ block.parent = parent; block.class_type = qual_class_type; process_overloaded_operator_arrow(operand, operator_position, tsn, depth+1, &block); } /* if */ } /* if */ end_of_routine:; } /* process_overloaded_operator_arrow */ #if MICROSOFT_EXTENSIONS_ALLOWED static void box_value_type_operand(an_operand *operand, a_boolean leave_as_handle) /* operand has a C++/CLI boxable type (possibly a fundamental type that corresponds to a C++/CLI value class type). Box the operand, producing an lvalue for the boxed value. If leave_as_handle is TRUE, produce a prvalue handle for the boxed value. In either case, this is an implicit operation. */ { an_expr_node_ptr expr; an_operand orig_operand; orig_operand = *operand; check_assertion(is_boxable_type(operand->type)); /* Convert the value to a prvalue. */ do_operand_transformations(operand, TOPT_NO_OPTIONS); expr = make_node_from_operand(operand); expr = add_box_to_expression(expr, /*is_implicit=*/TRUE, /*handle_to_form=*/FALSE); if (!leave_as_handle) { expr = add_indirection_to_node(expr); } /* if */ make_lvalue_or_rvalue_expression_operand(expr, operand); restore_operand_details(operand, &orig_operand); } /* box_value_type_operand */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ static a_symbol_ptr look_up_selection_name( a_symbol_locator *locator, a_type_ptr class_struct_union_type) /* Look up the second operand name in a selection operation by looking up the name given by locator in the class given by class_struct_union_type. Return NULL if the name is not found. */ { a_symbol_ptr member_sym; member_sym = class_qualified_id_lookup(locator, class_struct_union_type, (IDL_IS_EXPR_CONTEXT | IDL_IS_FIELD_SELECTION_OPERAND)); return member_sym; } /* look_up_selection_name */ static a_boolean check_valid_qualified_member_in_selection( a_symbol_locator *locator, a_source_position *diag_pos, a_type_ptr class_struct_union_type) /* locator describes the right-hand operand of a selection operator, and is either a qualified name or the result of a splice construct. Check to see that it is a member of class_struct_union_type, which is the class type of the left-hand operand, or of an appropriate base class thereof. Issue an error if not, at diag_pos (for a case like "A::x", diag_pos gives the position of the "A", whereas the position in the locator gives the position of the "x"). */ { a_boolean err = FALSE; check_assertion(!locator->is_vacuous_destructor_reference); if (is_error_locator(*locator)) { /* There was an error in the qualified name. */ err = TRUE; } else if (!is_class_struct_union_type(class_struct_union_type)) { err = TRUE; } else { a_symbol_ptr projection_member_sym = locator->specific_symbol; class_struct_union_type = skip_typerefs(class_struct_union_type); if (!projection_member_sym->is_class_member) { /* The qualified name is not the name of a class member (i.e., it's the name of a namespace member). */ if (expr_error_should_be_issued()) { pos_sy_error(ec_not_class_member, diag_pos, projection_member_sym); } /* if */ err = TRUE; } else if (class_struct_union_type-> variant.class_struct_union.is_nonreal_class || sym_parent_class(projection_member_sym)-> variant.class_struct_union.is_nonreal_class) { /* Skip the check for a nonreal class in a prototype instantiation. */ } else { /* Make sure the name is a member of the class indicated by the left-hand side, or one of its base classes. */ if (!is_same_class_or_base_class_thereof(class_struct_union_type, sym_parent_class( projection_member_sym))) { if (expr_error_should_be_issued()) { if (locator->is_splicer) { pos_syty_error(ec_splice_is_not_a_member_of_class, diag_pos, projection_member_sym, class_struct_union_type); } else { pos_ty_error(ec_name_not_member_of_class_or_base_classes, diag_pos, class_struct_union_type); } /* if */ } /* if */ err = TRUE; } /* if */ } /* if */ } /* if */ return !err; } /* check_valid_qualified_member_in_selection */ static a_boolean field_selection_class_can_be_incomplete( an_operand *operand_1, a_type_ptr type_1, a_boolean is_arrow_operator) /* Return TRUE if a member access operation where the first operand is *operand_1 (whose type after skipping typerefs is type_1) is valid even when the class type whose member being accessed is incomplete. */ { return (this_in_trailing_return_types_enabled || noexcept_enabled) && ((is_arrow_operator && is_this_parameter_operand(operand_1, (a_variable_ptr *)NULL)) || (type_1 != NULL && is_immediate_class_type(type_1) && class_symbol_supp(symbol_for(type_1))->being_defined)); } /* field_selection_class_can_be_incomplete */ static a_boolean looks_like_template_id_selection(void) /* The caller has determined that we are in a template context and that the current token is the first token of a member name followed by a "<" token. Return TRUE if that "<" can only be valid as the introducer for a template argument list (as opposed to a "less than" operator). This is used in some modes to handle a missing "template" keyword (as, e.g., in "x.template Y"). */ { a_boolean result = FALSE; a_token_sequence_number reparse_tsn = NO_TOKEN_SEQUENCE_NUMBER; a_token_cache cache; a_token_kind tok_kind; reparse_tsn = curr_token_sequence_number; begin_caching_fetched_tokens(/*include_curr_token=*/TRUE); do { tok_kind = curr_token; (void)get_token(); } while (tok_kind != tok_lt); /* For now, our heuristic is a typename followed by ">", ",", "*", "&" or "&&". */ if (curr_type_symbol(/*is_new_type_name=*/FALSE, /*in_prescan=*/TRUE, /*in_type_check=*/TRUE, /*is_implicit_type_context=*/FALSE, /*is_sizeof_context=*/FALSE) != NULL) { (void)get_token(); if (curr_token == tok_gt || curr_token == tok_comma || curr_token == tok_star || curr_token == tok_ampersand || curr_token == tok_and_and) { result = TRUE; } /* if */ } /* if */ copy_tokens_from_cache(curr_lexical_state_cache(), reparse_tsn, curr_token_sequence_number, /*include_last_token=*/FALSE, &cache); rescan_cached_tokens(&cache); end_caching_fetched_tokens(); return result; } /* looks_like_template_id_selection */ static void make_generic_splicer_selection(an_operand *opnd1, an_expr_node *splicer, a_boolean is_arrow, an_operand *result) /* We encountered an operation of the form opnd1.[: x :] or opnd1->[: x :] where x is instantiation-dependent. Build in *result a representation of the operation. */ { an_expr_node *obj = make_node_from_operand(opnd1), *selection; obj->next = splicer; selection = make_operator_node(is_arrow ? eok_points_to_field : eok_dot_field, type_of_unknown_templ_param_nontype, obj); make_expression_operand(selection, result); } /* make_generic_splicer_selection */ static void scan_expr_splicer(a_rescan_control_block *rcblock, an_operand *result); static void scan_selection_second_operand( an_operand *operand_1, a_type_ptr type_1, a_boolean is_arrow_operator, a_boolean offsetof_case, a_symbol_locator *locator, an_expr_node **splicer, a_type_ptr *updated_class_type, a_boolean *err) /* Scan and process the second operand of a selection operator, e.g., "x" in "p->x". operand_1 gives the first operand. type_1 gives the type of the first operand (with "pointer to" stripped off for the "->" case; in some vacuous destructor cases it may not be a class type, and it might be NULL in some error cases. It does have cv-qualifiers stripped off, however.). The operator is "->" if is_arrow_operator is TRUE. The selection is implicit in a builtin offsetof operation if offsetof_case is TRUE. The second operand is basically a name. Return a locator for that name in *locator. (For vacuous destructor cases, the locator will be set to describe the vacuous destructor name.) The locator describes the result of looking up the name in the first operand's class, not just the name in the abstract. *updated_class_type will be returned non-NULL if this routine wants to give the caller a new type to use for type_1 (that's used for some obscure pcc mode cases). If the selection is of the form [: x :] where x is instantiation-dependent, return a representation of x through *splicer. Set *err to TRUE if there is an error. */ { an_identifier_options_set gid_flags; a_source_position member_position, qualified_member_position; a_boolean is_qualified_name; *updated_class_type = NULL; /* See if an identifier (or equivalent) is next. */ gid_flags = GID_DTOR_RECOGNIZED | GID_IS_FIELD_SELECTION_OPERAND | GID_IS_EXPR_CONTEXT; if (C_dialect == C_dialect_cplusplus) { if (curr_token == tok_template) { /* A construct like "p->template f...". If it is allowed in this context, pass a flag to the identifier coalescing routine that the name is known to be a template. */ if (!cpp11_mode && !is_template_context()) { /* The template keyword, when used for syntactic disambiguation, may only appear within a template. */ expr_pos_diagnostic(strict_ansi_mode ? strict_ansi_discretionary_severity : es_warning, ec_template_not_in_template, &pos_curr_token); } else { gid_flags |= GID_FOLLOWS_TEMPLATE; } /* if */ (void)get_token(); } else if (microsoft_bugs && scope_stack_top().in_prototype_instantiation && curr_token == tok_identifier && next_token() == tok_lt) { /* MSVC doesn't parse templates in their generic form and therefore doesn't require a template prefix. We cannot reliably emulate that but we can handle many cases with a heuristic. */ if (looks_like_template_id_selection()) { gid_flags |= GID_FOLLOWS_TEMPLATE; } /* if */ } /* if */ /* In C++, explicit calls of destructors are allowed for simple types and classes without destructors. For example, p->int::~int(). */ gid_flags |= GID_VACUOUS_DTOR_RECOGNIZED; /* coverity[var_deref_model] */ if (*err || !is_class_struct_union_type(type_1)) { /* If the first operand is not a class, the vacuous destructor calls can be things like p->~int(). */ gid_flags |= GID_DTOR_MUST_BE_NONCLASS; } /* if */ } /* if */ if (is_generalized_identifier_start_full(gid_flags, type_1)) { a_symbol_ptr member_sym = NULL; a_boolean local_err; /* See if the name following the operator is a C++ qualified name, as in "p->A::x". */ /* Leading "::" is allowed as of the Portland X3J16/WG21 meeting; cfront always allowed it. */ is_qualified_name = coalesce_and_lookup_qualified_name(gid_flags, ilm_expr, &local_err); *err |= local_err; /* If the member is something like "A::x", member_position will give the position of the "x" and qualified_member_position will give the position of the "A". */ member_position = locator_for_curr_id.source_position; qualified_member_position = pos_curr_token; if (offsetof_case && is_dtor_like_locator(locator_for_curr_id)) { /* A destructor or C++/CLI finalizer name is not allowed as a field name for the offsetof case. */ expr_pos_error(ec_exp_identifier, &pos_curr_token); *err = TRUE; } else if (locator_for_curr_id.is_vacuous_destructor_reference) { /* We have something like p->int::~int, a reference to a vacuous destructor. Also p->A::~A(), where A is a class without a destructor. */ /* Watch out for error cases like p->int::~float. Also, in some error cases like p->~xxx, where xxx is either undefined or not a type name, class type will be NULL. */ if (is_error_locator(locator_for_curr_id) || qualifier_class_type(locator_for_curr_id) == NULL) { *err = TRUE; } /* if */ } else { /* Not a vacuous destructor case, i.e., normal case. */ a_boolean need_member_sym_check = TRUE; a_boolean operand_1_is_complete_class = type_1 != NULL && is_immediate_class_type(type_1) && !is_incomplete_type(type_1); if (!operand_1_is_complete_class && field_selection_class_can_be_incomplete(operand_1, type_1, is_arrow_operator)) { /* In C++11, "this" can be used in a late-specified return type, when the class of "this" is not complete. */ operand_1_is_complete_class = TRUE; } /* if */ /* Further checking beyond the fact that this is an identifier is not possible if there was an error in the first operand. */ if (operand_1_is_complete_class) { if (is_qualified_name) { qualified_name_check: /* There was a qualified member name, as in "p->A::x". "A" in the preceding must be the class pointed to by p or a base class thereof, i.e., "A::x" must be a member of the class of the first operand or of one of its base classes. */ check_assertion(type_1 != NULL); if (check_valid_qualified_member_in_selection( &locator_for_curr_id, &qualified_member_position, type_1)) { member_sym = fundamental_symbol_of( locator_for_curr_id.specific_symbol); } else { *err = TRUE; } /* if */ need_member_sym_check = FALSE; } else { /* Normal case: not qualified member name. */ if (locator_for_curr_id.is_destructor_name) { /* For destructors, don't attempt to look up the destructor name. The lookup will have been done, and the type verified earlier. If the class has a destructor, this is a real call; otherwise it is vacuous. An exception is made for dependent types. In such cases, a lookup is done as a nonreal member may be created. */ a_type_ptr dtor_type = locator_for_curr_id.variant.destructor_type; a_boolean dependent_op1, dependent_op2; a_class_symbol_supplement_ptr cssp_for_dtor; dependent_op1 = is_template_dependent_type(type_1); dependent_op2 = dtor_type != NULL && is_template_dependent_type(dtor_type); if (!dependent_op1 && !dependent_op2) { cssp_for_dtor = symbol_supplement_for_class(type_1); member_sym = cssp_for_dtor->destructor; } else if (!dependent_op1 && dependent_op2) { /* The destructor name is dependent (e.g., "~decltype(T())"), but the left operand is not dependent. It's safe to use a recorded destructor symbol in the proxy type. */ if (dtor_type->kind == (a_type_kind)tk_template_param) { dtor_type = proxy_class_for_template_param(dtor_type); } /* if */ cssp_for_dtor = symbol_supplement_for_class(dtor_type); if (cssp_for_dtor->destructor != NULL) { member_sym = cssp_for_dtor->destructor; } else { member_sym = create_proxy_or_nonreal_class_member_of_kind( dtor_type, (a_symbol_kind)sk_constant, IDL_NO_OPTIONS, &locator_for_curr_id); } /* if */ } else { /* The left operand is dependent. Create the destructor representation in the proxy class type of the left operand, but don't record it since other destructor expressions may be added there. */ member_sym = create_proxy_or_nonreal_class_member_of_kind( type_1, (a_symbol_kind)sk_constant, IDL_NO_OPTIONS, &locator_for_curr_id); } /* if */ locator_for_curr_id.specific_symbol = member_sym; } else { /* Look up this identifier in the scope of the class, struct, or union. */ check_assertion(type_1 != NULL); member_sym = look_up_selection_name(&locator_for_curr_id, type_1); } /* if */ if (member_sym == NULL && locator_for_curr_id.is_destructor_name) { /* This is a case like p->~A where the class has no destructor. This is a vacuous destructor case if the types match. Note that we do not allow ~A to be in a base class of the class pointed to by p, because destructor names are not inherited. The validity of the destructor name was checked when the identifier was coalesced. */ locator_for_curr_id.is_vacuous_destructor_reference = TRUE; locator_for_curr_id.parent.class_type = type_1; locator_for_curr_id.is_class_member = TRUE; need_member_sym_check = FALSE; } else if (member_sym != NULL && member_sym->kind == (a_symbol_kind)sk_class_template) { /* For a member template, coalesce the template reference. This will use the specific symbol already established. */ if (is_generalized_identifier_start(gid_flags)) { member_sym = coalesce_and_lookup_generalized_identifier( gid_flags, ilm_expr, &local_err); if (local_err) { *err = TRUE; } else if (locator_for_curr_id.is_qualified_name) { /* For qualified names, go back and do the normal checking. */ goto qualified_name_check; } /* if */ } else { unexpected_condition_str( "scan_selection_second_operand: template problem"); } /* if */ } /* if */ } /* if */ } /* if */ /* If the field was not found in pcc or SVR4 C mode, look for any field with that name. If there's only one (or several with the same offsets), cast the left-side variable to the right struct/union type and do the selection with the found field (otherwise, an error can be issued at this point). */ if (member_sym == NULL && (C_dialect == C_dialect_pcc || SVR4_C_mode) && /* Avoid the "rvalue . field" case. */ (is_arrow_operator || is_an_lvalue(operand_1))) { member_sym = other_field_with_same_name(); if (member_sym == NULL) { /* An error has been issued by other_field_with_same_name. */ *err = TRUE; /* Avoid issuing a second error below. */ need_member_sym_check = FALSE; } else { /* We found a field we can use. */ make_locator_for_symbol(member_sym, &locator_for_curr_id); locator_for_curr_id.source_position = member_position; if (is_arrow_operator) { /* "->" operator. */ expr_pos_warning(ec_old_fashioned_ptr_field_selection, &member_position); } else { /* "." operator. Convert the lvalue to an rvalue pointer, then use "->" instead. Note that the test above has ensured that operand_1 here is an lvalue. */ expr_pos_warning(ec_old_fashioned_field_selection, &member_position); take_address_of_lvalue(operand_1, (a_source_position*)NULL); is_arrow_operator = TRUE; } /* if */ /* Cast the pointer to a pointer to the proper struct or union. */ type_1 = sym_parent_class(member_sym); *updated_class_type = type_1; cast_operand(make_pointer_type(type_1), operand_1, /*is_implicit_cast=*/FALSE); /* Mark the struct or union type as referenced, since a field therein has been referenced. */ type_1->source_corresp.referenced = TRUE; } /* if */ } /* if */ if (member_sym == NULL && need_member_sym_check) { /* The identifier is not a member of the operand_1 class, struct, or union. */ *err = TRUE; expr_expect_error(); if (!operand_1_is_complete_class) { /* An error will be produced later because the first operand is not (a pointer to) a complete class, so do not issue an error here. */ } else if (is_error_locator(locator_for_curr_id)) { /* An error was previously issued. */ } else { if (expr_error_should_be_issued()) { pos_stsy_error(C_mode() ? ec_not_a_field : ec_not_a_member, &error_position, locator_for_curr_id.symbol_header->identifier, symbol_for(type_1)); } /* if */ /* Enter an undefined symbol and record a reference against it. */ { a_symbol_ptr undef_sym_ptr = enter_undefined_member_symbol(&locator_for_curr_id); record_symbol_reference((a_symbol_reference_kind)(SRK_REFERENCE | SRK_ERROR), undef_sym_ptr, &error_position, /*update_il_entry=*/FALSE); } } /* if */ } else if (constexpr_enabled && !constexpr_dynamic_alloc_enabled && is_dtor_like_locator(locator_for_curr_id) && !locator_for_curr_id.is_vacuous_destructor_reference && !is_template_dependent_type(type_1) && construct_not_allowed_in_cpp11_constant_expr( ec_expr_not_constant, &pos_curr_token)) { /* Vacuous destructor references are not allowed in C++11 constant expressions. */ } /* if */ } /* if */ /* The locator is copied back even on an error so that the caller can test the is_vacuous_destructor_reference flag. */ *locator = locator_for_curr_id; #if EXTRA_SOURCE_POSITIONS_IN_IL curr_construct_end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* Advance past the identifier. */ (void)get_token(); } else if (reflection_enabled && curr_token == tok_lsplice) { an_operand opnd2; clear_operand(ok_error, &opnd2); scan_expr_splicer((a_rescan_control_block*)NULL, &opnd2); if (operand_is_instantiation_dependent(&opnd2)) { *splicer = make_node_from_operand(&opnd2); } else if (is_sym_for_member_operand(&opnd2)) { make_locator_for_symbol(opnd2.symbol, locator); locator->source_position = opnd2.position; if (!check_valid_qualified_member_in_selection( locator, &opnd2.position, type_1)) { *err = TRUE; } /* if */ } else if (is_constant_operand(&opnd2)) { a_constant_ptr refl_cp = &opnd2.variant.constant; a_reflection_value *rvp = &refl_cp->variant.reflection; check_assertion(constant_is(refl_cp, ck_reflection)); if (rvp->entity.kind == iek_field) { a_field_ptr fp = (a_field*)rvp->entity.ptr; make_locator_for_symbol(symbol_for(fp), locator); locator->source_position = opnd2.position; } else if (rvp->entity.kind == iek_routine) { a_routine_ptr rp = (a_routine*)rvp->entity.ptr; make_locator_for_symbol(symbol_for(rp), locator); locator->source_position = opnd2.position; } else { expr_pos_error(ec_bad_reflection_kind_for_expression_splice, &opnd2.position, *rvp); *err = TRUE; } /* if */ if (!check_valid_qualified_member_in_selection( locator, &opnd2.position, type_1)) { *err = TRUE; } /* if */ } else if (is_expression_operand(&opnd2)) { an_expr_node *node2 = make_node_from_operand(&opnd2); if (node_is(node2, enk_routine)) { a_routine_ptr rp = node_routine(node2); make_locator_for_symbol(symbol_for(rp), locator); locator->source_position = opnd2.position; } else if (node_is(node2, enk_field)) { a_field_ptr fp = node_field(node2); make_locator_for_symbol(symbol_for(fp), locator); locator->source_position = opnd2.position; } else if (node_is(node2, enk_variable)) { a_variable_ptr vp = node_variable(node2); make_locator_for_symbol(symbol_for(vp), locator); locator->source_position = opnd2.position; } else { expr_pos_error(ec_cannot_splice_member_access, &opnd2.position); *err = TRUE; } /* if */ } else { check_assertion(is_error_operand(&opnd2)); *err = TRUE; } /* if */ locator->is_splicer = TRUE; } else { clear_locator(locator, &pos_curr_token); #if EXTRA_SOURCE_POSITIONS_IN_IL curr_construct_end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* The identifier is not present; error. */ (void)required_token(tok_identifier, C_dialect == C_dialect_cplusplus ? ec_exp_member_name : ec_exp_field_name); *err = TRUE; } /* if */ } /* scan_selection_second_operand */ static a_symbol_ptr symbol_for_template_param_unknown_entity_rescan( a_constant_ptr con, a_rescan_control_block *rcblock, an_expr_rescan_info_entry_ptr eriep, a_boolean *is_template_id, a_template_arg_ptr *expl_templ_arg_list) /* con is a template param constant for an unknown entity (e.g., an overloaded function). Do template substitution on it using the information in rcblock and eriep, and produce a symbol for the substituted result. If the result is a template-id (i.e., it has explicit template arguments), return *is_template_id TRUE and *expl_templ_arg_list set to the template argument list. */ { a_symbol_ptr sym; a_ctws_options_set options; *is_template_id = FALSE; *expl_templ_arg_list = NULL; check_assertion(con->kind == (a_constant_repr_kind)ck_template_param); if (con->variant.template_param.kind == (a_template_param_constant_kind)tpck_address) { /* For a tpck_address, process the underlying member. */ con = con->variant.template_param.variant.constant; } else if (con->variant.template_param.kind == (a_template_param_constant_kind)tpck_template_ref) { /* For a tpck_template_ref, process the underlying tpck_unknown_function or tpck_member, and also return the template argument list to the caller. */ *is_template_id = TRUE; *expl_templ_arg_list = con->variant.template_param.variant.template_ref.arg_list; con = con->variant.template_param.variant.template_ref.con; } /* if */ check_assertion(con->variant.template_param.kind == (a_template_param_constant_kind)tpck_member || con->variant.template_param.kind == (a_template_param_constant_kind)tpck_destructor || con->variant.template_param.kind == (a_template_param_constant_kind)tpck_unknown_function); options = rcblock->options; if (eriep->saved_operand.is_name_followed_by_left_paren) { options |= CTWS_IS_CALL_CONTEXT; } /* if */ sym = symbol_for_template_param_unknown_entity_con_after_substitution( con, rcblock->template_arg_list, rcblock->template_param_list, &eriep->saved_operand.position, rcblock->ctws_state, options); return sym; } /* symbol_for_template_param_unknown_entity_rescan */ void record_template_arg_operand(a_template_arg_ptr tap, an_expr_node_ptr expr) /* Record the given expression as an argument operand for the given nontype template argument. Clear any constant the template argument might point to. */ { tap->variant.constant = NULL; tap->arg_operand = alloc_arg_operand(); make_expression_operand(expr, &tap->arg_operand->operand); } /* record_template_arg_operand */ void transfer_arg_operand_for_template_arg(a_template_arg_ptr tap, a_template_arg_ptr orig_tap) /* tap is a template argument created in a rescan by doing substitution on orig_tap. If it is a nontype template argument, add an arg_operand entry to it based on the rescan info recorded in orig_tap. This can be called from outside of the expression processing routines. */ { if (is_nontype_templ_arg(tap) && !tap->is_array_bound_of_unknown_type) { an_operand_ptr operand; check_assertion(is_nontype_templ_arg(orig_tap) && !orig_tap->is_array_bound_of_unknown_type); check_assertion(tap->arg_operand == NULL); tap->arg_operand = alloc_arg_operand(); operand = &tap->arg_operand->operand; make_constant_operand(tap->variant.constant, operand); if (cpp11_sfinae_enabled) { an_expr_rescan_info_entry_ptr eriep = orig_tap->variant.constant->rescan_info; if (eriep != NULL) { restore_operand_info_from_expr_rescan_info_entry(operand, eriep); if (expr_stack != NULL && expr_stack->possible_rescan_context) { /* For a constant that may be rescanned to do semantic analysis later for template deduction, save extra information from the operand. */ a_constant_ptr constant = tap->variant.constant; constant->rescan_info = save_operand_info_in_rescan_info_entry( operand, constant->rescan_info); } /* if */ } /* if */ } /* if */ } /* if */ } /* transfer_arg_operand_for_template_arg */ a_template_arg_ptr copy_template_arg_list_with_substitution_rebuilding_arg_operands( a_symbol_ptr template_sym, a_template_arg_ptr arg_list_to_copy, a_template_param_ptr param_list_for_copy, a_template_arg_ptr templ_arg_list, a_template_param_ptr templ_param_list, a_source_position *source_pos, a_ctws_options_set options, a_boolean *copy_error, a_ctws_state_ptr ctws_state) /* Wrapper for copy_template_arg_list_with_substitution that additionally rebuilds the arg_operand information in the copied template argument list, so that it can be used as an argument list for a function template reference. This can be called from outside of the expression processing routines. */ { a_template_arg_ptr new_tap; options |= CTWS_COPY_ARG_OPERAND_INFO; /* A partial argument list is okay for function templates and cases where we don't know the template. It is not okay for non-function templates (e.g., classes, variables). */ if (template_sym == NULL || is_function_or_template_symbol(template_sym) || ctws_state->preserve_deduced_packs) { options |= CTWS_PARTIAL_ARG_LIST_OKAY; } /* if */ new_tap = copy_template_arg_list_with_substitution( template_sym, arg_list_to_copy, param_list_for_copy, (a_template_param_ptr)NULL, templ_arg_list, templ_param_list, source_pos, options, copy_error, ctws_state); return new_tap; } /* copy_template_arg_list_with_substitution_rebuilding_arg_operands */ static void rescan_locator_template_arg_list( a_symbol_locator *locator, a_rescan_control_block *rcblock, a_boolean indefinite_function) /* Do substitution on and rescan the explicit template argument list attached to the given locator. If indefinite_function is TRUE, the symbol in the given locator is not necessarily the template to be associated with the template arguments (argument-dependent lookup may still find a different template). */ { a_template_arg_ptr rescan_orig_templ_arg_list = locator->template_arg_list; a_template_param_ptr rescan_orig_templ_param_list = NULL; a_symbol_ptr sym = locator->specific_symbol; a_boolean copy_error = FALSE; check_assertion(sym != NULL); sym = fundamental_symbol_of(sym); if (symbol_is(sym, sk_function_template)) { if (indefinite_function) { /* This is similar to the overloaded function case below. */ sym = NULL; } else { rescan_orig_templ_param_list = sym->variant.template_info->variant. function.decl_cache->decl_info->parameters; } /* if */ } else if (symbol_is(sym, sk_variable_template)) { rescan_orig_templ_param_list = sym->variant.template_info->variant. variable.decl_cache->decl_info->parameters; } else if (symbol_is(sym, sk_class_template)) { if (!sym->variant.template_info->is_nonreal_member) { rescan_orig_templ_param_list = sym->variant.template_info-> cache->decl_info->parameters; } /* if */ } else if (symbol_is(sym, sk_overloaded_function)) { /* An overload set. Fail substitution if it does not contain a template. Otherwise, don't pass a symbol for substitution below. */ copy_error = !symbol_is_or_contains_template(sym); sym = NULL; } else if (symbol_is(sym, sk_constant)) { /* A constant. Fail substitution if it is not a ck_template_param constant. Otherwise, don't pass a symbol for substitution below. */ a_constant_ptr con = sym->variant.constant; sym = NULL; copy_error = !constant_is(con, ck_template_param); } else if (symbol_is(sym, sk_undefined)) { /* Undefined symbols may be created to represent calls that require argument-dependent lookup during rescan. */ sym = NULL; } else { /* Not a template symbol. Fail substitution. */ copy_error = TRUE; } /* if */ if (!copy_error) { locator->template_arg_list = copy_template_arg_list_with_substitution_rebuilding_arg_operands( sym, rescan_orig_templ_arg_list, rescan_orig_templ_param_list, rcblock->template_arg_list, rcblock->template_param_list, &locator->source_position, rcblock->options, ©_error, rcblock->ctws_state); } /* if */ if (copy_error) { locator->is_error = TRUE; record_suppressed_error(); subst_fail(rcblock->error_detected); } /* if */ } /* rescan_locator_template_arg_list */ static a_symbol_ptr symbol_for_nonreal_closure_call_operator_rescan( a_constant_ptr con, a_type_ptr parent_class) /* con is a tpck_expression constant used as the operand in a generic selection expression being rescanned. parent_class is the substituted class in which the rescanned selection should occur. If con represents the address of a function (under an implicit cast, likely), return the symbol for the corresponding function in parent_class. This currently only occurs for a case like template concept X = [] static { return true; }(); static_assert(X); where the lambda call in the rescannable context is made template-dependent by wrapping the address of the operator() in a tpck_expression entry. */ { a_symbol_ptr result = NULL; an_expr_node_ptr expr; a_constant_ptr addr_con; a_routine_ptr rp; a_symbol_locator loc; check_assertion(constant_is(con, ck_template_param) && tpck_is(con, tpck_expression)); if (!is_class_struct_union_type(parent_class)) { goto done; } /* if */ expr = expr_node_from_tpck_expression(con); if (expr == NULL) goto done; expr = strip_implicit_operations_for_rescan( expr, (an_expr_rescan_info_entry_ptr*)NULL); if (!is_constant_node(expr)) { goto done; } addr_con = node_constant(expr); if (!constant_is(addr_con, ck_address) || !address_base_is(addr_con, abk_routine)) { goto done; } /* if */ rp = addr_con->variant.address.variant.routine; check_assertion(rp != NULL && rp->source_corresp.is_class_member); /* Pattern matched -- produce the symbol. */ make_locator_for_symbol(symbol_for(rp), &loc); clear_specific_symbol(loc); result = look_up_selection_name(&loc, parent_class); done: return result; } /* symbol_for_nonreal_closure_call_operator_rescan */ static void get_locator_for_rescanned_selection_second_operand( a_type_ptr class_struct_union_type, a_rescan_control_block *rcblock, a_boolean call_rescan_case, a_boolean *followed_by_left_paren, a_symbol_locator *locator, a_boolean *err) /* A selection operator is being rescanned to redo semantic analysis. rcblock->expr is the selection expression. The first operand has already been retrieved, and its underlying class type is given by class_struct_union_type (NULL if there is an error in the first operand, possibly a non-class type for a vacuous destructor reference). Produce a symbol locator for the second operand, which is basically a name, and put that in *locator. The locator describes the result of looking up the name in the first operand's class, not just the name in the abstract. If call_rescan_case is TRUE, rcblock->expr is a member call, and the locator produced is for its first operand, the function. Set *err to TRUE if there is an error. Return *followed_by_left_paren TRUE if the member name was followed by a left parenthesis in the source. */ { an_expr_node_ptr expr = rcblock->expr, op1, member_op; an_expr_rescan_info_entry_ptr eriep; an_expr_rescan_info_entry rescan_info; a_symbol_ptr sym; a_boolean is_qualified = FALSE; a_boolean is_conversion_func = FALSE; a_boolean is_template_id = FALSE; a_template_arg_ptr expl_templ_arg_list = NULL; an_expr_operator_kind op; a_boolean need_member_sym_check = TRUE; a_source_position *qualified_member_position = NULL; *followed_by_left_paren = FALSE; if (class_struct_union_type == NULL) { /* Some previous error on the first operand. */ set_to_error_locator(*locator); *err = TRUE; goto end_of_routine; } /* if */ check_assertion(expr != NULL && is_operation_node(expr)); op1 = expr->variant.operation.operands; if (call_rescan_case && is_call_node(expr)) { member_op = op1; } else { member_op = op1->next; } /* if */ if (member_op != NULL) { eriep = get_expr_rescan_info(member_op, &rescan_info); is_qualified = eriep->saved_operand.is_qualified_name; qualified_member_position = &eriep->saved_operand.position; *followed_by_left_paren = eriep->saved_operand.is_name_followed_by_left_paren; } else { /* Cases like vacuous destructor calls have no second operand. */ eriep = get_expr_rescan_info(expr, &rescan_info); } /* if */ op = expr->variant.operation.kind; switch (op) { case eok_dot_field: case eok_points_to_field: /* A field selection like this names a concrete field. Get the symbol for it and make a locator. */ check_assertion(member_op != NULL && member_op->kind == (an_expr_node_kind)enk_field); if (is_error_operand(&eriep->saved_operand)) { *err = TRUE; subst_fail(rcblock->error_detected); clear_locator(locator, qualified_member_position); } else { /* Get the symbol from the saved operand, because that's the projection symbol. */ sym = eriep->saved_operand.symbol; check_assertion(sym != NULL); sym = copy_parent_type_with_substitution(sym, sym->parent.class_type, rcblock->template_arg_list, rcblock->template_param_list, qualified_member_position, /*is_type=*/FALSE, (a_type_ptr *)NULL, rcblock->options, err, rcblock->ctws_state); make_locator_for_symbol(sym, locator); } /* if */ break; case eok_dot_member_call: case eok_points_to_member_call: check_assertion(call_rescan_case); FALLTHROUGH case eok_dot_static: case eok_points_to_static: /* Rescan the second operand, producing an operand that essentially names something. Things like parentheses and lvalue-to-rvalue conversion code should be dropped off. */ sym = NULL; check_assertion(member_op != NULL); member_op = strip_implicit_operations_for_rescan( member_op, (an_expr_rescan_info_entry_ptr *)NULL); if (is_constant_node(member_op)) { /* Most template cases come across as ck_template_param constants which we can do substitution on and produce a symbol. */ a_constant_ptr con = node_constant(member_op); if (constant_is(con, ck_template_param)) { a_constant_ptr member_con = NULL; a_boolean is_template_ref = FALSE; if (tpck_is(con, tpck_member) || tpck_is(con, tpck_destructor)) { if (has_name(con) && unmangled_name_of(&con->source_corresp)[0] == '~') { /* For a destructor name, deal with the various cases. */ if (!is_class_struct_union_type(class_struct_union_type)) { /* The left operand is not a class type, so this is a vacuous destructor case. */ goto handle_vacuous_destructor_call; } else { a_class_symbol_supplement_ptr cssp = symbol_supplement_for_class( class_struct_union_type); if (cssp->destructor == NULL) { /* The class doesn't have a destructor, so this is a vacuous destructor case. */ goto handle_vacuous_destructor_call; } else { /* The class does have a destructor. We don't need to look it up the usual way. */ sym = cssp->destructor; need_member_sym_check = FALSE; goto have_symbol; } /* if */ } /* if */ } /* if */ member_con = con; } else if (tpck_is(con, tpck_template_ref)) { /* For a tpck_template_ref, check the underlying tpck_unknown_function or tpck_member to see if we're dealing with a class member. */ member_con = con->variant.template_param.variant.template_ref.con; if (!member_con->source_corresp.is_class_member) { member_con = NULL; } /* if */ is_template_ref = TRUE; } else if (tpck_is(con, tpck_unknown_function)) { if (con->source_corresp.is_class_member) { member_con = con; } /* if */ } else if (tpck_is(con, tpck_expression)) { /* This can happen in a case like: template concept C = [] static { return true; }(); static_assert(C); where the called operator() has a nondependent type but the closure class is nonreal (since it appears in a rescannable context). */ sym = symbol_for_nonreal_closure_call_operator_rescan( con, class_struct_union_type); goto have_symbol; } /* if */ if (member_con != NULL) { /* See if we need to do a lookup to find the member. */ a_type_ptr parent_type = parent_class_of(member_con); check_assertion(member_con->kind == (a_constant_repr_kind)ck_template_param); if (member_con->variant.template_param.kind == (a_template_param_constant_kind)tpck_unknown_function) { if (member_con->variant.template_param.is_qualified_name) { is_qualified = TRUE; } /* if */ if (member_con->variant.template_param .variant.unknown_function.conversion_type != NULL) { is_conversion_func = TRUE; } /* if */ } /* if */ if (class_type_supp(parent_type)->proxy_of_type != NULL) { /* The parent type is a proxy class for a template parameter. Substitute the original template parameter for the proxy class. */ parent_type = class_type_supp(parent_type)->proxy_of_type; } /* if */ /* For an unqualified name or a member of an unknown class, look up the member name in the actual class of the first operand. (Conversion functions, however, are not found through lookup.) */ if ((!is_qualified || parent_type == type_of_unknown_templ_param_nontype) && !is_conversion_func) { a_type_ptr lookup_type = skip_typerefs(class_struct_union_type); /* Lookup is possible if lookup_type is not incomplete or if it is in the process of being defined (i.e., it has an associated scope). (It is not sufficient to check for there to be an associated scope, because nonreal proxy types may not have an associated scope, but they are considered complete.) */ if (is_immediate_class_type(lookup_type) && (!lookup_type->incomplete || !scope_is_null_or_placeholder(class_type_supp(lookup_type) ->assoc_scope)) && symbol_for(member_con) != NULL) { check_assertion(qualified_member_position != NULL); clear_locator(locator, qualified_member_position); locator->symbol_header = symbol_for(member_con)->header; sym = look_up_selection_name(locator, class_struct_union_type); if (sym != NULL) { if (locator->specific_symbol->ambiguous) { /* If a projection symbol was created, it may refer to an ambiguous member. sym is already reduced in such cases, but the locator still points to the original projection symbol. Pretend we found nothing so that a SFINAE failure is recorded. */ sym = NULL; } else if (symbol_is(sym, sk_class_template)) { if (!is_template_ref) { /* This is a class template, but there's no explicit template argument list. Pretend we found nothing. */ sym = NULL; } /* if */ } else if (symbol_is_or_contains_function_template(sym) || (symbol_is(sym, sk_constant) && constant_is(sym->variant.constant, ck_template_param) && (sym->variant.constant ->variant.template_param.kind == tpck_member || (sym->is_nonreal_member && sym->variant.constant ->variant.template_param.kind == tpck_unknown_function)))) { /* Function templates are okay with or without an explicit template argument list. (For dependent members, assume they could be a function template; likewise, a nonreal member recorded as an unknown function might be a function template.) */ if (is_template_ref) { /* Something like "p.template f()". */ is_template_id = TRUE; expl_templ_arg_list = con->variant.template_param .variant.template_ref.arg_list; } /* if */ } else if (is_template_ref) { /* A non-template with an explicit template argument list. Pretend we found nothing. */ sym = NULL; } /* if */ if (sym != NULL) { /* We have found an acceptable symbol, but as it has already been reduced, we need to get the original symbol, as that might be a projection symbol. */ sym = locator->specific_symbol; } /* if */ } /* if */ need_member_sym_check = FALSE; } /* if */ goto have_symbol; } /* if */ } /* if */ } /* if */ if (!is_class_struct_union_type(class_struct_union_type)) { *err = TRUE; subst_fail(rcblock->error_detected); clear_locator(locator, qualified_member_position); break; } /* if */ /* Do substitution on the constant and produce a symbol. */ sym = symbol_for_template_param_unknown_entity_rescan( con, rcblock, eriep, &is_template_id, &expl_templ_arg_list); if (is_conversion_func && sym != NULL && sym->is_unknown_function && symbol_is(sym, sk_constant)) { a_constant_ptr conv_con = sym->variant.constant; if (constant_is(conv_con, ck_template_param) && tpck_is(conv_con, tpck_unknown_function)) { /* If con represents a conversion function that is a member of an unknown type, we may have substituted the conversion type but the unknown parent type is still unknown. Now that we have a concrete conversion type, we can look up the conversion operator in class_struct_union_type. */ a_type_ptr conv_type = conv_con->variant.template_param .variant.unknown_function.conversion_type; sym = look_up_conversion_function(class_struct_union_type, conv_type, &expr->position); } /* if */ } /* if */ } else if (is_variable_node(member_op)) { /* Static data member. */ a_variable_ptr var = node_variable(member_op); if (var->is_nonreal) { sym = copy_template_variable_with_substitution( var, rcblock->template_arg_list, rcblock->template_param_list, &rcblock->expr->position, rcblock->options, err, rcblock->ctws_state); } else { sym = symbol_for(var); } /* if */ } else if (is_routine_node(member_op)) { /* Static member function or lambda call operator. */ a_routine_ptr rp = node_routine(member_op); if (rp->source_corresp.is_class_member && is_immediate_class_type(class_struct_union_type) && is_lambda_closure_type(class_struct_union_type) && parent_class_of(rp) != skip_typerefs(class_struct_union_type)) { /* The object operand is a rescanned lambda closure with a newly created closure class; look up the corresponding member (such as operator()) in the new closure class. */ a_symbol_locator loc; clear_locator(&loc, &locator->source_position); loc.symbol_header = symbol_for(rp)->header; sym = look_up_selection_name(&loc, class_struct_union_type); if (rp->is_template_function && rp->expl_template_arg_list_used && rp->template_arg_list != NULL) { is_template_id = TRUE; expl_templ_arg_list = copy_template_arg_list(rp->template_arg_list); } /* if */ } else if (rp->assoc_template != NULL) { sym = copy_template_routine_with_substitution( rp, rcblock->template_arg_list, rcblock->template_param_list, &rcblock->expr->position, rcblock->options, err, rcblock->ctws_state); } else { sym = symbol_for(node_routine(member_op)); } /* if */ } else { unexpected_condition(); } /* if */ have_symbol: if (sym != NULL) { make_locator_for_symbol(sym, locator); locator->is_qualified_name = is_qualified; if (is_template_id) { locator->is_template_id = TRUE; locator->template_arg_list = expl_templ_arg_list; } /* if */ } else { *err = TRUE; subst_fail(rcblock->error_detected); check_assertion(qualified_member_position != NULL); clear_locator(locator, qualified_member_position); } /* if */ break; case eok_dot_vacuous_destructor_call: case eok_points_to_vacuous_destructor_call: /* A vacuous destructor call. class_struct_union_type gives the substituted "destructor class" type (which might not be a class type at all). */ handle_vacuous_destructor_call: clear_locator(locator, &null_source_position); locator->is_vacuous_destructor_reference = TRUE; locator->is_class_member = TRUE; locator->parent.class_type = class_struct_union_type; if (!is_class_struct_union_type(class_struct_union_type)) { locator->is_nonclass_destructor = TRUE; } /* if */ need_member_sym_check = FALSE; break; default: unexpected_condition_str("bad selection operator in rescan"); } /* switch */ #if EXTRA_SOURCE_POSITIONS_IN_IL if (member_op != NULL) { locator->source_position = member_op->expr_range.start; } else #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* Do not insert code here. */ { locator->source_position = eriep->saved_operand.position; } /* if */ /* For a qualified id case, make sure the id is a member of the class or one of its base classes. */ if (need_member_sym_check && !*err && is_qualified) { if (!check_valid_qualified_member_in_selection(locator, &locator->source_position, class_struct_union_type)) { *err = TRUE; } /* if */ } /* if */ if (!*err && locator->template_arg_list != NULL) { /* Do substitution on the explicit template argument list. */ rescan_locator_template_arg_list(locator, rcblock, /*indefinite_function=*/FALSE); } /* if */ end_of_routine:; } /* get_locator_for_rescanned_selection_second_operand */ static a_boolean force_indefinite_function_in_skipped_decltype( a_symbol_ptr sym) /* Return TRUE if the indicated symbol should be treated as an indefinite function because we're in a context where some compilers treat certain calls as dependent even though they are actually nondependent. Treating the function as indefinite allows us to get to the code in overload resolution that does the special handling. */ { a_boolean force_indefinite = FALSE; reduce_projection_symbol_to_fundamental_symbol(sym); if (is_simple_function_symbol(sym) && is_skipped_decltype_context()) { force_indefinite = TRUE; } /* if */ return force_indefinite; } /* force_indefinite_function_in_skipped_decltype */ static a_boolean left_paren_follows_right_parens(void) /* Return TRUE if the tokens ahead consist of one or more right parentheses followed by a left parenthesis. */ { a_boolean result = FALSE; if (curr_token == tok_rparen) { a_scanning_token_cache cache; do { cache_curr_token(cache.ptr()); (void)get_token(); } while (curr_token == tok_rparen); if (curr_token == tok_lparen) { result = TRUE; } /* if */ rescan_cached_tokens(cache.ptr()); } /* if */ return result; } /* left_paren_follows_right_parens */ static void scan_field_selection_operator( an_operand *operand_1, a_rescan_control_block *rcblock, a_boolean call_rescan_case, a_boolean offsetof_case, an_operand *result, an_operand *bound_function_selector) /* Scan the "." and "->" operators. *operand_1 is the left operand. The current token is the operator. Scan the member name operand, combine the two operands into an expression, and return an operand for that in *result. If the result is a bound function, return the selector in *bound_function_selector. If rcblock is non-NULL, redo semantic analysis on a previously-scanned expression, and return the result in *result and *bound_function_selector (or an error indication in *rcblock). operand_1 is expected to be NULL in that case. If call_rescan_case is TRUE, the rescan is of the implied selection under a member call operator (e.g., eok_dot_member_call). rcblock->expr points to the call, whose second operand is the first operand of the selection, and whose first operand is the second operand of the selection. rcblock->operator_token in that case indicates the proper token for the selection, not an operator token for the call. This routine is also called to parse a __builtin_offsetof field construct (offsetof_case will be TRUE in that case). */ { a_boolean is_arrow_operator; a_type_ptr class_struct_union_type = NULL; a_type_ptr orig_class_struct_union_type = NULL; a_boolean err = FALSE; a_boolean need_operand_1_type_check = FALSE; a_boolean allow_constant_selection = FALSE; a_ref_entry_ptr rep; a_boolean is_vacuous_destructor_reference = FALSE; a_boolean force_indefinite_function = FALSE; a_boolean force_unknown_dependent_function = FALSE; a_boolean member_name_followed_by_left_paren = FALSE; a_source_position member_position; a_boolean pcc_mode_integral_pointer_case = FALSE; an_operand local_operand_1; a_token_kind operator_token; a_source_position operator_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ a_token_sequence_number operator_tok_seq_number; a_symbol_locator locator; a_boolean cppcli_overloaded_case = FALSE; db_enter(4, "scan_field_selection_operator"); if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. */ operator_token = rcblock->operator_token; check_assertion(operand_1 == NULL); operand_1 = &local_operand_1; /* Pick up the first operand now. The second (basically a name) will be deciphered a bit later. For the call rescan case, this picks up the second operand of the call and returns it as operand_1. */ make_selection_rescan_operands(rcblock, operand_1, call_rescan_case, offsetof_case, &operator_position, &operator_tok_seq_number); } else { /* Normal, non-rescan, processing. */ operator_token = curr_token; operator_position = pos_curr_token; operator_tok_seq_number = curr_token_sequence_number; } /* if */ /* Remember if this was an arrow or a dot selector. */ is_arrow_operator = (operator_token == tok_arrow); if (curr_expr_kind_is_traditional_const() || (microsoft_mode && constexpr_enabled)) { if (curr_expr_kind_is(ek_pp)) { /* Field selection not allowed in preprocessor expression. */ expr_pos_error(ec_bad_pp_operator, &pos_curr_token); err = TRUE; } else if (curr_expr_kind_is(ek_integral_constant) || curr_expr_kind_is(ek_template_arg)) { /* Field selection is not allowed in integral constant expressions or template argument expressions. */ if (current_mode_allows_field_selection_folding()) { /* ... except in certain modes, where something like struct A { enum { e1 = 1 }; } a; int x[a.e1]; is allowed. The constant check is done at the end. */ allow_constant_selection = TRUE; } else if (curr_expr_kind_is(ek_integral_constant)) { /* Field selection not allowed in integral constant expression. */ expr_pos_error(ec_bad_integral_operator, &pos_curr_token); err = TRUE; } else { /* Field selection not allowed in a template argument expression. */ expr_pos_error(ec_bad_templ_arg_expr_operator, &pos_curr_token); err = TRUE; } /* if */ } else if (curr_expr_kind_is(ek_init_constant) && !C_mode() && is_arrow_operator && current_mode_allows_field_selection_folding() && (is_an_lvalue(operand_1) || (is_a_prvalue(operand_1) && !is_constant_operand(operand_1)))) { /* In certain C++ modes, a->e1 can be used as a constant if e1 is a constant member (like an enumerator). The "a" expression requires a dereference (test just done), so this selection will get an error in the normal processing. Suppress the dereference of the first operand in exchange for a requirement that the result is a constant (in which case the left operand would not have to be evaluated). */ allow_constant_selection = TRUE; } /* if */ } /* if */ if (err) { /* Operation is not allowed in this kind of expression. */ operand_will_not_be_used_because_of_error(operand_1); } else { #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled) { if (is_cli_generic_definition_argument_type(operand_1->type)) { /* Uses of C++/CLI generic parameters have to use the "->" form. */ if (is_arrow_operator) { /* Okay, but box the operand if it is value-constrained. */ if (!is_handle_type(operand_1->type)) { box_value_type_operand(operand_1, /*leave_as_handle=*/TRUE); } /* if */ } else { /* Error, have to use "->". */ expr_pos_error(ec_generic_selection_with_points_to, &operator_position); if (is_handle_type(operand_1->type)) { is_arrow_operator = TRUE; } /* if */ } /* if */ } /* if */ if (!is_arrow_operator && !is_class_struct_union_type(operand_1->type)) { /* In C++/CLI, the first operand of "." will be boxed if it has a built-in type that has a corresponding value class type. */ if (is_boxable_type(operand_1->type)) { box_value_type_operand(operand_1, /*leave_as_handle=*/FALSE); } /* if */ } /* if */ } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (is_arrow_operator && C_dialect == C_dialect_cplusplus) { /* Process overloaded operator->, if applicable. */ process_overloaded_operator_arrow(operand_1, &operator_position, operator_tok_seq_number, (a_nondependent_call_depth)1, (an_operator_arrow_block_ptr)NULL); } /* if */ { an_expression_kind saved_expr_kind; a_boolean saved_traditional; a_boolean need_restore = FALSE; if (allow_constant_selection && is_arrow_operator) { /* For the cfront or Microsoft case that allows p->k in a constant expression, treat the "p" momentarily as part of a non-constant expression to get no error on the lvalue-to-rvalue conversion. */ temporarily_set_non_constant_expression_kind(&saved_expr_kind, &saved_traditional); need_restore = TRUE; } /* if */ /* Do implicit operand transformations. In the "." case, keep a glvalue if we have one. */ do_operand_transformations(operand_1, is_arrow_operator ? TOPT_NO_OPTIONS : TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION); if (need_restore) { restore_constant_expression_kind(saved_expr_kind, saved_traditional); } /* if */ } /* The left operand must be (a pointer to) a struct or union. */ if (is_error_operand(operand_1)) { err = TRUE; normalize_error_operand(operand_1); } else { if (is_arrow_operator) { /* "->" operator. The left operand must be a pointer (or a C++/CLI handle). */ #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && is_literal_convertible_to_cli_string(operand_1, /*allow_complex=*/TRUE)) { /* When the field selection operator is applied to a string literal in C++/CLI, the literal is immediately converted to System::String^ */ convert_operand_to_handle_to_cli_string(operand_1); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (C_dialect == C_dialect_pcc && is_integral_or_enum_type(operand_1->type)) { /* In pcc mode, something like 0->x is valid. */ pcc_mode_integral_pointer_case = TRUE; } else if (is_template_param_or_nonreal_class_type( skip_reference_type(operand_1->type))) { /* In a prototype instantiation, allow a template parameter type, which might be a pointer type. Also allow a nonreal class type, which might have an operator-> function. */ orig_class_struct_union_type = type_of_unknown_templ_param_nontype; } else { a_boolean ptr_okay = TRUE; if (!cli_or_cx_enabled) { ptr_okay = check_pointer_operand( operand_1, ec_member_access_requires_pointer); #if MICROSOFT_EXTENSIONS_ALLOWED } else { ptr_okay = check_pointer_or_handle_operand( operand_1, ec_expr_not_pointer_nor_handle); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ if (ptr_okay) { orig_class_struct_union_type = type_pointed_to(operand_1->type); } else { /* Not a pointer. */ err = TRUE; } /* if */ } /* if */ } else { /* "." operator. */ orig_class_struct_union_type = operand_1->type; if (microsoft_mode && !C_mode() && is_an_rvalue(operand_1) && is_class_struct_union_type(orig_class_struct_union_type) && is_expression_operand(operand_1)) { /* MSVC++ treats a field selection off a function call returning a POD type as an lvalue. Note that f().i is an rvalue but A().i (where A is a POD class name) is not. */ an_expr_node_ptr op_1 = skip_parens(operand_1->variant.expression); if (is_operation_node(op_1)) { an_expr_operator_kind op = op_1->variant.operation.kind; /* eok_dot_member_call and eok_points_to_member_call are deliberately excluded here because MSVC++ does this trick only with non-member calls. (Checked in MSVC++ 7.1.) */ if (op == (an_expr_operator_kind)eok_call) { if (symbol_supplement_for_class(orig_class_struct_union_type) ->is_cpp03_POD) { revert_class_prvalue_to_glvalue_if_possible( operand_1, /*xvalue=*/rvalue_references_enabled); } /* if */ } else if (op == (an_expr_operator_kind)eok_question) { /* A selection from a class rvalue "?" is also considered an lvalue, whether or not the class is a POD. */ revert_microsoft_rvalue_to_lvalue_if_possible(operand_1); } /* if */ } /* if */ } /* if */ if (is_an_lvalue(operand_1)) using_lvalue(operand_1); } /* if */ } /* if */ /* Check that the left operand is (a pointer to) a complete class, struct, or union, for either operator. */ if (!err && !pcc_mode_integral_pointer_case) { /* Drop any qualifiers or typedefs on the class/struct/union type. */ class_struct_union_type = skip_typerefs(orig_class_struct_union_type); make_proxy_type_if_needed: if (type_is(class_struct_union_type, tk_pointer) && class_struct_union_type->variant.pointer.is_reference) { class_struct_union_type = skip_typerefs(class_struct_union_type->variant.pointer.type); } /* if */ if (type_is(class_struct_union_type, tk_template_param)) { /* For a template parameter type, switch to the corresponding proxy class, except that if the type is a nonreal type corresponding to a prototype instantiation the prototype instantiation should be used. Preserve cv-qualifiers on the type. */ a_boolean done = FALSE; a_symbol_ptr csym = symbol_for(class_struct_union_type); if (csym != NULL && csym->is_nonreal_nested_type) { a_type_ptr nested_type = type_symbol_type(csym->corresp_nonreal_or_nested_type); if (!nested_type->incomplete && scope_of_class_is_active(nested_type)) { class_struct_union_type = nested_type; done = TRUE; } /* if */ } /* if */ if (!done) { a_type_qualifier_set qualifiers = get_type_qualifiers(orig_class_struct_union_type); class_struct_union_type = proxy_class_for_template_param(class_struct_union_type); orig_class_struct_union_type = make_qualified_type(class_struct_union_type, qualifiers); } /* if */ } /* if */ if (is_immediate_class_type(class_struct_union_type)) { /* Instantiate the class if it is a template class. */ complete_class_type_is_needed(class_struct_union_type); if (class_struct_union_type->incomplete && scope_is_null_or_placeholder( class_type_supp(class_struct_union_type)->assoc_scope) && is_template_dependent_context() && rcblock == NULL && (gpp_mode || clang_mode || microsoft_mode)) { class_struct_union_type = type_of_unknown_templ_param_nontype; goto make_proxy_type_if_needed; } /* if */ } /* if */ /* No error is issued yet if the first operand is not (a pointer to) a class, because (a) C++ allows p->int::~int(), and (b) prototype instantiations. (pcc mode also allows fields to be selected from non-class pointers and integral values, but that case doesn't get here.) */ need_operand_1_type_check = TRUE; } /* if */ } /* if */ if (rcblock == NULL) { a_type_ptr updated_class_type; an_expr_node *splicer = NULL; /* Advance past the operator and scan the second operand. */ (void)get_token(); scan_selection_second_operand(operand_1, class_struct_union_type, is_arrow_operator, offsetof_case, &locator, &splicer, &updated_class_type, &err); #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = curr_construct_end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ member_name_followed_by_left_paren = (curr_token == tok_lparen); if (updated_class_type != NULL) { /* The subroutine asks that the class type be updated at this level. This is used in pcc mode for an obscure feature. */ check_assertion(C_dialect == C_dialect_pcc || SVR4_C_mode); orig_class_struct_union_type = updated_class_type; class_struct_union_type = skip_typerefs(updated_class_type); } /* if */ if (splicer != NULL) { /* The selection is of a dependent splicer. Create a generic representation for the resulting selection operations. */ make_generic_splicer_selection(operand_1, splicer, is_arrow_operator, result); goto done; } /* if */ } else { /* Redoing semantic analysis on a previously-scanned selection. */ get_locator_for_rescanned_selection_second_operand( class_struct_union_type, rcblock, call_rescan_case, &member_name_followed_by_left_paren, &locator, &err); #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = rcblock->expr->expr_range.end; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* if */ /* Note here that the is_vacuous_destructor_reference flag is valid even if the error flag is also set, and it is intentional that we check it anyway. */ if (locator.is_vacuous_destructor_reference) { need_operand_1_type_check = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED if (locator.is_finalizer_name) { error_in_operand(ec_finalizer_does_not_exist, operand_1); err = TRUE; } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here */ { is_vacuous_destructor_reference = TRUE; } /* if */ } /* if */ if (need_operand_1_type_check) { a_type_ptr tp = class_struct_union_type; a_boolean is_class_type = is_immediate_class_type(tp); if (!is_class_type || tp->incomplete) { /* The first operand is not (a pointer to) a complete class, struct, or union. This check was delayed to this point so that we could allow things like vacuous destructor references. */ if (is_class_type && field_selection_class_can_be_incomplete(operand_1, tp, is_arrow_operator)) { /* In C++11, "this" can be used in a late-specified return type, when the class of "this" is not complete. */ } else { if (!is_error_type(class_struct_union_type)) { an_error_code err_code; /* If the problem is that the class is incomplete, use a different error message. */ a_type_ptr err_type = operand_1->type; if (is_incomplete_type(class_struct_union_type) && is_class_struct_union_type(class_struct_union_type)) { err_type = class_struct_union_type; err_code = is_arrow_operator ? ec_ptr_or_ref_to_incomplete_type : ec_incomplete_type_not_allowed; } else { if (C_dialect == C_dialect_cplusplus) { err_code = is_arrow_operator ? ec_expr_not_ptr_to_class : ec_expr_not_class; } else { err_code = is_arrow_operator ? ec_expr_not_ptr_to_struct_or_union : ec_expr_not_struct_or_union; } /* if */ } /* if */ type_error_in_operand(err_code, operand_1, err_type); } /* if */ err = TRUE; } /* if */ } /* if */ } /* if */ /* Set the error position to the starting position. */ copy_source_position(operand_1->position, error_position); if (err || is_error_operand(operand_1)) { /* If the operator is not allowed in this kind of expression, or if there was an error in the first operand, make an error operand out of the result. */ make_error_operand(result); operand_will_not_be_used_because_of_error(operand_1); } else if (is_vacuous_destructor_reference) { an_expr_node_ptr node; a_type_ptr dtor_type = locator.parent.class_type; /* A reference to a destructor for a class or simple type that does not have one, e.g., p->int::~int(). */ if (!is_class_struct_union_type(dtor_type)) { /* For a non-class case, make sure the operand type reflects the type used to refer to the "destructor". This allows the C++-generating back end to deal with a case like typedef int T; int *p; p->T::~T(); and get the name "T" in the output. */ /* We force the type into the expression even when it's equivalent to what's already there, so we can't use cast_operand here. */ a_type_ptr new_type = is_arrow_operator ? make_pointer_type(dtor_type) : dtor_type; a_type_ptr object_type = operand_1->type; an_operand orig_operand; if (is_arrow_operator && object_type->kind == (a_type_kind)tk_pointer) { object_type = type_pointed_to(object_type); } /* if */ /* The object expression of a vacuous destructor call must be a scalar type (or a GNU vector type). */ if (is_error_type(object_type) || is_scalar_type(object_type)) { /* Okay. */ #if GNU_VECTOR_TYPES_ALLOWED } else if (is_vector_type(object_type)) { /* Okay. */ #endif /* GNU_VECTOR_TYPES_ALLOWED */ } else if (gpp_version_is(<= 40200) && is_array_type(object_type)) { /* g++ accepts array types through version 4.2. */ } else if (microsoft_mode && ms_permissive) { /* Microsoft's permissive mode allows all non-class types. */ } else if (expr_error_should_be_issued()) { pos_ty_error(ec_non_scalar_vacuous_dtor_call, &operand_1->position, object_type); } /* if */ orig_operand = *operand_1; node = make_node_from_operand(operand_1); if (!identical_types(node->type, new_type)) { /* There is an actual type change here (probably one involving cv-qualifiers). */ if (is_glvalue_node(node)) { node = add_cast_to_glvalue(node, new_type); } else { node = add_cast(node, new_type); } /* if */ } else { /* No actual type change, but update the type anyway to make sure we have the right "spelling" of the type, with typedef if needed. */ node->type = new_type; } /* if */ make_lvalue_or_rvalue_expression_operand(node, operand_1); restore_operand_details_incl_ref(operand_1, &orig_operand); } else { /* Class case. */ dtor_type = skip_typerefs(dtor_type); check_assertion(class_struct_union_type != NULL && is_immediate_class_type(class_struct_union_type)); if (!is_incomplete_type(class_struct_union_type) && !identical_types(class_struct_union_type, dtor_type) && !class_struct_union_type->variant.class_struct_union. is_nonreal_class && !dtor_type->variant.class_struct_union.is_nonreal_class) { /* Cast to a base class in a case like struct A {}; struct B : public A {}; B *p; p->A::~A(); */ a_base_class_ptr bcp = find_base_class_of(class_struct_union_type, dtor_type); check_assertion(bcp != NULL); base_class_cast_operand(operand_1, bcp, (a_type_ptr)NULL, /*check_cast_access=*/TRUE, /*allow_ambiguity=*/FALSE, /*is_implicit_cast=*/TRUE, /*implicit_in_naming=*/FALSE, /*is_object_pointer=*/TRUE); } /* if */ } /* if */ /* Make a vacuous destructor call node and an operand for it. */ node = make_node_from_operand(operand_1); node = make_operator_node((an_expr_operator_kind)(is_arrow_operator ? eok_points_to_vacuous_destructor_call : eok_dot_vacuous_destructor_call), void_type(), node); make_expression_operand(node, result); set_operand_id_details_from_locator(result, &locator); if (!(rcblock == NULL ? (member_name_followed_by_left_paren || (!strict_ansi_mode && left_paren_follows_right_parens())) : (call_rescan_case || is_vacuous_dtor_call_node(rcblock->expr)))) { /* A pseudo-destructor must be called. In the rescan case, there are two cases: (1) the form p->~T() and (2) the form p->T::~T(). (Either case could use the "dot" notation, too.) The first form will be an eok_dot_static/eok_points_to_static node before substitution, and "call_rescan_case" will be set to TRUE if we are under a call node. The second case is already a vacuous call node, but we will have checked that it is being called when it was first parsed (i.e., before substitution). The standard does not permit the pseudo- destructor to be parenthesized, but other compilers do seem to permit it; we follow suit in non-strict modes. */ an_error_severity sev = es_error; if (gpp_version_is(<70400) && rcblock != NULL) { /* Older versions of GCC apparently accept this in nested SFINAE contexts. We approximate this by accepting it if there is more than one sck_function_access rescan entry on the scope stack. */ a_scope_depth sd, cnt = 0; for (sd = 0; sd<=depth_scope_stack; ++sd) { if (scope_is(&scope_stack[sd], sck_function_access) && scope_stack[sd].is_rescan) { ++cnt; } /* if */ } /* for */ if (cnt > 1) sev = es_warning; } /* if */ expr_pos_diagnostic(sev, ec_vacuous_destructor_not_called, &locator.source_position); } /* if */ } else { /* Normal selection, not a vacuous destructor reference. */ /* Record that the field was referenced, for cross-reference (etc.) purposes. */ /* Don't do this if the symbol is an overloaded function (we don't yet know which function is being called). */ a_symbol_ptr projection_member_sym = locator.specific_symbol; a_symbol_ptr member_sym = fundamental_symbol_of(projection_member_sym); member_position = locator.source_position; if (scope_is(&scope_stack_top(), sck_func_prototype) && is_function_or_template_symbol(member_sym) && class_struct_union_type != NULL && is_immediate_class_type(class_struct_union_type) && class_struct_union_type->variant.class_struct_union .is_prototype_instantiation && !is_dtor_like_locator(locator)) { /* When scanning a member function declaration a subtlety can arise with a construct like "auto g()->decltype(this->f());" in prototype instantiations. The in-class declaration might be able to resolve f to a specific member, while a matching out-of-class declaration may not be able to do so if additional members f have been added to the parent class. The difference in the representation of the return type for both declarations of g() would cause the front end to be unable to match the two. Therefore, we force "this->f" to be resolved as an unknown dependent function in all such cases. */ rep = NULL; force_indefinite_function = TRUE; force_unknown_dependent_function = TRUE; } else if (symbol_is(member_sym, sk_overloaded_function)) { rep = NULL; } else if (member_sym->potentially_overloaded) { /* Force a member function that might or might not be overloaded in a prototype instantiation because of a using-declaration to be treated as overloaded. */ rep = NULL; force_indefinite_function = TRUE; } else if (force_indefinite_function_in_skipped_decltype(member_sym)) { /* Make all calls dependent in certain decltype contexts. */ rep = NULL; force_indefinite_function = TRUE; #if MICROSOFT_EXTENSIONS_ALLOWED } else if (cli_or_cx_enabled && member_name_followed_by_left_paren && hide_by_sig_lookup_applies(projection_member_sym)) { /* In C++/CLI mode, a symbol for which hide-by-sig lookup applies has to be processed through overload resolution even if it doesn't look overloaded. */ rep = NULL; force_indefinite_function = TRUE; cppcli_overloaded_case = TRUE; } else if (symbol_is(member_sym, sk_property_set)) { rep = NULL; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else if (rcblock != NULL && is_simple_function_symbol(member_sym) && member_sym->variant.routine.ptr->is_deleted) { subst_fail(rcblock->error_detected); rep = NULL; } else if (is_simple_function_symbol(member_sym) && has_explicit_this_parameter( skip_typerefs(routine_symbol_type(member_sym)))) { rep = NULL; force_indefinite_function = TRUE; } else { rep = ref_entry(member_sym, &member_position); } /* if */ if (!cppcli_overloaded_case) { /* Do ambiguity and access control checking on the member. For overloaded functions, this checks ambiguity but not access (which can be different for each function in the set). */ expr_check_ambiguity_and_verify_access(&locator); } /* if */ if (is_error_locator(locator)) { /* Some error in ambiguity or access control checking. */ make_error_operand(result); /* Avoid further diagnostics by making this an error reference. This is necessary because with something like x.y where y is ambiguous, some versions of y might be nonstatic and some static, which means we do not know whether x is really used. */ change_operand_refs_to_error(operand_1); change_refs_to_error(rep); } else { if (offsetof_case) { /* For offsetof, only a field is allowed. */ if (is_nontype_template_param_symbol(member_sym)) { /* A template-dependent case: We cannot tell yet whether the access will resolve to a field. */ } else if (member_sym->kind != (a_symbol_kind)sk_field) { expr_pos_error(ec_offsetof_nonfield, &member_position); make_error_operand(result); goto after_switch; } else if (is_any_reference_type( member_sym->variant.field.ptr->type)) { expr_pos_error(ec_offsetof_ref_field, &member_position); make_error_operand(result); goto after_switch; } else if (member_sym->variant.field.ptr->is_bit_field) { expr_pos_error(ec_offsetof_bit_field, &member_position); make_error_operand(result); goto after_switch; } /* if */ } /* if */ /* See what kind of member we have. */ switch (member_sym->kind) { case sk_field: /* Normal field selection. */ /* This operation uses the left-side operand, so cast the operand to the type of the member symbol. */ cast_pointer_for_field_selection( operand_1, is_arrow_operator, member_sym, projection_member_sym, (a_boolean)locator.access_control_error_reported, /*do_protected_member_check=*/TRUE, &member_position); do_field_selection_operation(operand_1, orig_class_struct_union_type, is_arrow_operator, /*compiler_generated=*/FALSE, &locator, &member_position, end_position_or_null(&end_position), rep, result); break; case sk_static_data_member: case sk_variable: /* Static data member or variable template reference. */ { a_variable_ptr var = variable_for_symbol(member_sym); if (!var->used && var->is_template_variable && !var->is_nonreal && !var->is_specialized && is_void_type(var->type)) { /* Partial instantiation of a variable template can produce a void type, but that is not diagnosed right away because explicit specialization can change the type. */ pos_syty_error(ec_void_template_variable, &locator.source_position, member_sym, var->type); make_error_operand(result); #if MICROSOFT_EXTENSIONS_ALLOWED } else if (var->property_or_event_descr != NULL) { /* A C++/CLI static event variable. (Static properties are handled via sk_property_set symbols.) */ check_assertion(cli_or_cx_enabled && property_or_event_kind_is(var, pek_cli_event)); make_event_ref_operand(member_sym, operand_1, is_arrow_operator, result); set_operand_id_details_from_locator(result, &locator); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else { /* A normal static data member (not an event or property). */ make_lvalue_variable_operand( var, &member_position, end_position_or_null(&end_position), result, rep); set_operand_id_details_from_locator(result, &locator); result->is_name_followed_by_left_paren = member_name_followed_by_left_paren; combine_unneeded_selector_with_operand(operand_1, is_arrow_operator, result); member_name_followed_by_left_paren = FALSE; } /* if */ } break; #if MICROSOFT_EXTENSIONS_ALLOWED case sk_property_set: /* One or more C++/CLI properties. */ check_assertion(cli_or_cx_enabled); make_property_ref_operand(member_sym, operand_1, is_arrow_operator, result); set_operand_id_details_from_locator(result, &locator); break; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ case sk_member_function: /* Member function (static or non-static). */ a_type_ptr rtp; rtp = routine_symbol_type(member_sym); if (force_indefinite_function || routine_type_is_nonstatic_member_function(rtp)) { /* Nonstatic member function. */ if (!force_indefinite_function && has_uninstantiated_default_arg(rtp)) { force_indefinite_function = TRUE; } /* if */ /* Also continue here for an overloaded function or a member template. */ nonstatic_member_function: /* Such a reference is not allowed in an initializer constant expression. */ if (curr_expr_kind_is(ek_init_constant) && !constexpr_enabled) { error_and_make_error_operand(ec_expr_not_constant, result); } else { if (!force_indefinite_function && member_sym->kind == (a_symbol_kind)sk_member_function) { /* For a simple non-overloaded function, adjust the selector to the proper class. We don't do this for the cases that go through overload resolution, since that adjustment is done there (it might not be done if a static member function is selected). */ cast_pointer_for_field_selection(operand_1, is_arrow_operator, member_sym, projection_member_sym, (a_boolean)locator. access_control_error_reported, /*do_protected_member_check=*/TRUE, &member_position); } /* if */ /* Make an operand for the function with the selector bound to it. */ if (member_sym->kind == (a_symbol_kind)sk_overloaded_function || member_sym->kind == (a_symbol_kind)sk_function_template || force_indefinite_function) { /* Overloaded function or member template. */ make_indefinite_function_operand(projection_member_sym, &locator, result); if (force_unknown_dependent_function) { conv_indefinite_function_to_unknown_dependent_function( result, /*force_to_rvalue=*/FALSE); /* Calls to unknown dependent member functions are represented with the eok_dot_static/eok_points_to_static operator under an ordinary call node. */ goto treat_as_static_member; } /* if */ } else { /* Non-overloaded function. */ make_function_designator_operand(projection_member_sym, (a_boolean)locator. is_qualified_name, /*compiler_generated=*/FALSE, &member_position, end_position_or_null( &end_position), rep, result); set_operand_name_reference_from_locator(result, &locator); } /* if */ copy_operand(operand_1, bound_function_selector) /*lint !e413*/; bind_member_function_operand_to_selector(bound_function_selector, is_arrow_operator, result); } /* if */ } else { /* Static member function. */ make_function_designator_operand(projection_member_sym, (a_boolean)locator. is_qualified_name, /*compiler_generated=*/FALSE, &member_position, end_position_or_null( &end_position), rep, result); set_operand_name_reference_from_locator(result, &locator); treat_as_static_member: result->is_name_followed_by_left_paren = member_name_followed_by_left_paren; combine_unneeded_selector_with_operand(operand_1, is_arrow_operator, result); member_name_followed_by_left_paren = FALSE; } /* if */ break; case sk_overloaded_function: /* Overloaded function. Only overloaded member functions are possible here, since we've checked that the member symbol is part of the left operand class or one of its base classes. */ #if CHECKING if (!member_sym->is_class_member) { internal_error( "scan_field_selection_operator: overloaded func not member"); } /* if */ #endif /* CHECKING */ goto nonstatic_member_function; case sk_function_template: /* Member function template. */ goto nonstatic_member_function; case sk_class_template: /* Class template. Returned by symbol lookup for cases like x.template f in prototype instantiations. A class template is returned because there's only a representation for the class case as a member of a nonreal class, but it's really a function template. */ check_assertion_or_expect_error(locator.is_template_id && is_template_dependent_context()); make_unknown_dependent_function_operand(projection_member_sym, /*is_template_id=*/TRUE, locator.template_arg_list, (a_boolean)locator. is_qualified_name, result); change_template_param_constant_operand_to_lvalue(result); combine_unneeded_selector_with_operand(operand_1, is_arrow_operator, result); break; case sk_constant: /* Member constant (e.g., an enumerator). */ make_sym_constant_operand(member_sym, result); set_operand_id_details_from_locator(result, &locator); result->position = member_position; #if EXTRA_SOURCE_POSITIONS_IN_IL result->end_position = end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ result->is_name_followed_by_left_paren = member_name_followed_by_left_paren; combine_unneeded_selector_with_operand(operand_1, is_arrow_operator, result); member_name_followed_by_left_paren = FALSE; break; case sk_type: case sk_class_or_struct_tag: case sk_union_tag: case sk_enum_tag: /* The identifier is a type identifier. */ expr_pos_error(ec_type_identifier_not_allowed, &member_position); operand_will_not_be_used_because_of_error(operand_1); make_error_operand(result); break; case sk_variable_template: /* The identifier is a variable template name. */ if (expr_error_should_be_issued()) { pos_sy_error(ec_missing_template_arg_list, &member_position, member_sym); } /* if */ operand_will_not_be_used_because_of_error(operand_1); make_error_operand(result); break; default: unexpected_condition_str( "scan_field_selection_operator: bad symbol kind"); } /* switch */ after_switch:; } /* if */ } /* if */ /* The position of the operand is the position of the selection except when the operand is a bound function, in which case it's the position of the function name (because the selector has its own operand). */ if (result->bound_function) { check_assertion(!is_vacuous_destructor_reference); set_operand_position(result, &member_position, &end_position, (a_source_position *)NULL); result->is_name_followed_by_left_paren= member_name_followed_by_left_paren; } else { #if GNU_EXTENSIONS_ALLOWED if (gpp_version_is(<70200) || clang_mode) { /* GCC and Clang treat selections on the current instantiation as dependent in expression contexts. */ if (class_struct_union_type != NULL && is_immediate_class_type(class_struct_union_type) && class_struct_union_type ->variant.class_struct_union.is_prototype_instantiation && !is_template_dependent_type(result->type)) { result->type = type_of_unknown_templ_param_nontype; if (is_expression_operand(result)) { result->variant.expression->type = result->type; } else if (is_constant_operand(result)) { result->variant.constant.type = result->type; } /* if */ } /* if */ } /* if */ #endif /* GNU_EXTENSIONS_ALLOWED */ /* Not a bound function; the operand position reflects the entire selection. */ set_operand_position(result, &operand_1->position, &end_position, &operator_position); } /* if */ if (allow_constant_selection && !constexpr_enabled) { /* If we are allowing field selection in a constant expression as an extension, check now that the result is constant and has an appropriate type (assuming the C++03 model of constant expressions; for the C++11 "constexpr" model, this is done later if needed). allow_constant_selection TRUE means we suppressed an error earlier in exchange for this check at the end. Note that there are other cases of selections allowed in constant expressions that don't have the flag set. */ a_boolean bad = TRUE; if ((curr_expr_kind_is(ek_init_constant) || curr_expr_kind_is(ek_template_arg)) && is_an_lvalue(result)) { /* In these expression kinds, an lvalue can be left as is, because its address might be taken. */ bad = FALSE; } else { /* Otherwise, the result must be a constant of integral or enum type. The conversion to prvalue exposes any underlying constant, and also drops the field selection part leaving only the right operand. */ conv_glvalue_to_prvalue(result); if (is_constant_operand(result)) { if (is_integral_or_enum_type(result->type)) { bad = FALSE; } /* if */ } /* if */ } /* if */ if (bad) { if (!is_error_operand(result)) { error_in_operand(ec_expr_not_constant, result); } /* if */ } /* if */ } else { /* A field selection rules out an integral constant expression. */ rule_out_expr_kinds(ROEK_INTEGRAL_CONSTANT, result); } /* if */ done: record_operator_position_in_rescan_info(result, &operator_position, operator_tok_seq_number, (a_source_position *)NULL); db_exit(); } /* scan_field_selection_operator */ static void combine_ptr_to_member_operands( an_operand *opnd1, an_operand *opnd2, a_boolean is_arrow_operator, a_source_position *operator_position, an_operand *result, an_operand *bound_function_selector) /* Combine *opnd1 and the pointer-to-member *opnd2 into a pointer-to-member selection ("E1.*E2" if is_arrow_operator is FALSE and "E1->*E2" otherwise) and return an operand for the result in *result. operator_position is the position of the selection operator, used for diagnostics. When the result is a bound member function, return its selector in *bound_function_selector. The operands are assumed not to be of (possibly) template-dependent type and not to involve operator overloading; those cases are handled by the caller. */ { a_boolean err = FALSE; a_type_ptr operand_1_type = NULL, qual_operand_1_type = NULL; a_type_ptr opnd2_type, qual_opnd2_type, opnd2_class, result_type; a_base_class_ptr bcp = NULL; an_expr_node_ptr select_node, object_node, pm_node; a_boolean ptr_to_data_member_case = FALSE; a_boolean result_is_a_glvalue = FALSE, result_is_an_xvalue = FALSE; /* Do implicit operand transformations. In the ".*" case, keep a glvalue if we have one. */ do_operand_transformations(opnd1, is_arrow_operator ? TOPT_NO_OPTIONS : TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION); /* Check the first operand type. It must be (a pointer to) a class. */ if (is_error_operand(opnd1)) { err = TRUE; normalize_error_operand(opnd1); } else { if (is_arrow_operator) { /* "->*" operator. The first operand must be a pointer (except in C++/CX mode, where it could be a handle). */ a_boolean operand1_type_okay; #if MICROSOFT_EXTENSIONS_ALLOWED if (cppcx_enabled) { operand1_type_okay = check_pointer_or_handle_operand( opnd1, ec_expr_not_pointer_nor_handle); } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do no insert code here. */ { operand1_type_okay = check_pointer_operand( opnd1, ec_member_access_requires_pointer); } /* if */ if (operand1_type_okay) { qual_operand_1_type = type_pointed_to(opnd1->type); operand_1_type = skip_typerefs(qual_operand_1_type); } else { /* Not a pointer. */ err = TRUE; } /* if */ } else { /* ".*" operator. */ qual_operand_1_type = opnd1->type; operand_1_type = skip_typerefs(qual_operand_1_type); if (cpp11_mode && !microsoft_mode && !gpp_version_is(<100000) && is_a_prvalue(opnd1) && is_immediate_class_type(operand_1_type)) { conv_class_prvalue_operand_to_glvalue(opnd1, /*xvalue=*/TRUE); } /* if */ if (is_a_glvalue(opnd1)) { using_lvalue(opnd1); } /* if */ } /* if */ if (!err && !is_immediate_class_type(operand_1_type)) { /* Not (a pointer to) a class. */ an_error_code err_code; err_code = is_arrow_operator ? ec_expr_not_ptr_to_class : ec_expr_not_class; type_error_in_operand(err_code, opnd1, opnd1->type); err = TRUE; } /* if */ } /* if */ /* The type of the second operand must be pointer to member. */ do_operand_transformations(opnd2, TOPT_NO_OPTIONS); qual_opnd2_type = opnd2->type; opnd2_type = skip_typerefs(qual_opnd2_type); if (!type_is(opnd2_type, tk_ptr_to_member)) { if (!type_is(opnd2_type, tk_error)) { error_in_operand(ec_expr_not_ptr_to_member, opnd2); } /* if */ err = TRUE; } /* if */ if (!err) { /* Check the combination of the operand types. */ /* The class underlying the second operand type must be the same as the class of the first operand, or a base class thereof. The check that the derivation is unambiguous and accessible is done later. */ opnd2_class = pm_class_type(opnd2_type); if (same_entities(operand_1_type, opnd2_class)) { /* Same class. */ bcp = NULL; } else if ((bcp = find_base_class_of(operand_1_type, opnd2_class)) != NULL) { /* Related classes. */ } else { /* Bad combination. */ expr_pos_ty2_error(ec_incompatible_ptr_to_member_selection_operands, operator_position, operand_1_type, opnd2_class); err = TRUE; } /* if */ } /* if */ if (!err) { /* Check for invalid combinations with C++11 ref-qualifiers. */ a_type_ptr member_type = pm_member_type(opnd2_type); member_type = skip_typerefs(member_type); if (type_is(member_type, tk_routine)) { a_ref_qualifier_kind ref_qual = enum_cast( rout_type_supp(member_type)->ref_qualifiers); if (ref_qual == rqk_rvalue) { if (is_arrow_operator || is_an_lvalue(opnd1)) { err = TRUE; if (expr_error_should_be_issued()) { pos_ty_error(ec_pm_call_obj_not_rvalue, operator_position, opnd2_type); } /* if */ } /* if */ } else if (ref_qual == rqk_lvalue) { if (!is_arrow_operator && is_an_rvalue(opnd1) && !(rvalue_allowed_with_const_qual_memptr && rout_type_supp(member_type)->qualifiers == TQ_CONST)) { err = TRUE; if (expr_error_should_be_issued()) { pos_ty_error(ec_pm_call_obj_not_lvalue, operator_position, opnd2_type); } /* if */ } /* if */ } /* if */ } else { ptr_to_data_member_case = TRUE; } /* if */ } /* if */ if (err) { /* Some error. */ make_error_operand(result); operand_will_not_be_used_because_of_error(opnd1); operand_will_not_be_used_because_of_error(opnd2); } else { /* The operands are compatible. Determine the value category of the result. C++03 had this to say: "The result of a .* expression is an lvalue only if its first operand is an lvalue and its second operand is a pointer to data member. The result of an ->* expression is an lvalue only if its second operand is a pointer to data member." C++11 changed that significantly: "The result of a .* expression whose second operand is a pointer to a data member is of the same value category (3.10) as its first operand. The result of a .* expression whose second operand is a pointer to a member function is a prvalue." And C++14 revised it again (via the resolution to Core issue 616): "The result of a .* expression whose second operand is a pointer to a data member is an lvalue if the first operand is an lvalue and an xvalue otherwise. The result of a .* expression whose second operand is a pointer to a member function is a prvalue." (In C++11 and C++14, "E1->*E2" is by definition "(*(E1)).*E2".) */ if (selection_from_prvalue_is_xvalue && !microsoft_mode) { if (ptr_to_data_member_case) { result_is_a_glvalue = TRUE; result_is_an_xvalue = !is_arrow_operator && !is_an_lvalue(opnd1); } /* if */ } else if (cpp11_mode && !microsoft_mode) { if (ptr_to_data_member_case) { if (is_arrow_operator || is_an_lvalue(opnd1)) { result_is_a_glvalue = TRUE; } else if (is_an_xvalue(opnd1)) { result_is_a_glvalue = TRUE; result_is_an_xvalue = TRUE; } /* if */ } /* if */ } else { if (is_arrow_operator || (is_an_lvalue(opnd1) || is_error_operand(opnd1))) { /* The result is an lvalue if the operator is "->*" or if the first operand is an lvalue (or might be, if an error). */ result_is_a_glvalue = TRUE; } else if (any_cfront_mode() || ms_version_is(<1900)) { /* ARM rules: the result is always an lvalue and that doesn't depend on the value category of the left operand. */ /* Also the case for MSVC up to 18.x. */ result_is_a_glvalue = TRUE; /* Force the "->*" form to get an lvalue result. */ conv_selector_to_object_pointer(opnd1, &is_arrow_operator); } else if (is_an_xvalue(opnd1)) { /* xvalue.*pm produces an xvalue result. */ result_is_a_glvalue = TRUE; result_is_an_xvalue = TRUE; } else { /* prvalue.*pm, result is a prvalue. */ result_is_a_glvalue = FALSE; } /* if */ } /* if */ /* Cast the left operand to a base class if necessary. This does the ambiguity and accessibility checking. */ if (bcp != NULL) { base_class_cast_operand(opnd1, bcp, (a_type_ptr)NULL, /*check_cast_access=*/TRUE, /*allow_ambiguity=*/FALSE, /*is_implicit_cast=*/TRUE, /*implicit_in_naming=*/FALSE, /*is_object_pointer=*/TRUE); } /* if */ /* Determine the result type, which is roughly the member type pointed to by the second operand. */ result_type = make_pm_selection_type(qual_operand_1_type, opnd2_type); if (is_function_type(result_type)) { /* Result is a bound function. It can only be called or (as an anachronism) cast to a normal function pointer. */ copy_operand(opnd2, result); /* Clear the reference list for the second operand, because if any pointers-to-members are in there we don't want to change the references from address-taken to reference on a call. */ result->ref_entries_list = NULL; copy_operand(opnd1, bound_function_selector); bind_member_function_operand_to_selector(bound_function_selector, is_arrow_operator, result); } else { /* Result is a data member. */ /* Use an eok_pm_field node with the pointer to object and pointer-to-member as the operands. */ object_node = make_node_from_operand(opnd1); pm_node = make_node_from_operand(opnd2); object_node->next = pm_node; select_node = make_operator_node( is_arrow_operator ? eok_pm_points_to_field : eok_pm_field, result_type, object_node); if (strict_cpp17_eval_order) { select_node->variant.operation.eval_left_to_right = TRUE; } /* if */ make_expression_operand(select_node, result); if (result_is_a_glvalue) { /* The result is a glvalue. */ select_node->is_lvalue = TRUE; set_glvalue_operand_state(result); /* Keep the references from the first operand. */ result->ref_entries_list = opnd1->ref_entries_list; if (result_is_an_xvalue) { conv_rvalue_reference_result_to_xvalue(result); } /* if */ } else { /* A prvalue result. The type must be complete. */ complete_type_is_needed(result_type); if (is_incomplete_type(result_type)) { expr_issue_incomplete_type_diag(operator_position, result_type); conv_to_error_operand(result); result_type = result->type; } /* if */ } /* if */ /* There shouldn't be any pointers to members to references. */ check_assertion(!is_any_reference_type(result_type)); } /* if */ } /* if */ } /* combine_ptr_to_member_operands */ static void scan_ptr_to_member_operator( an_operand *operand_1, a_rescan_control_block *rcblock, a_boolean call_rescan_case, an_operand *result, an_operand *bound_function_selector) /* Scan the ".*" and "->*" operators. *operand_1 is the left operand. The current token is the operator. Scan the second operand, combine the two operands into an expression, and return an operand for that in *result. If the result is a bound function, return the selector in *bound_function_selector. If rcblock is non-NULL, redo semantic analysis on a previously-scanned expression, and return the result in *result and *bound_function_selector (or an error indication in *rcblock). operand_1 is expected to be NULL in that case. If call_rescan_case is TRUE, the rescan is of the implied selection under a pointer-to-member call operator (e.g., eok_dot_pm_call). rcblock->expr points to the call, whose second operand is the first operand of the selection. The first operand of the call is the second operand of the selection, and it has been rescanned already and is provided in *result (that's copied and used as the second operand, and then the result is built in the original *result operand). rcblock->operator_token in that case indicates the proper token for the selection, not an operator token for the call. */ { a_token_kind operator_token; a_boolean is_arrow_operator; a_boolean err = FALSE, processed = FALSE; an_operand local_operand_1, operand_2; a_source_position operator_position; a_token_sequence_number operator_tok_seq_number; db_enter(4, "scan_ptr_to_member_operator"); if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. */ operator_token = rcblock->operator_token; check_assertion(operand_1 == NULL); operand_1 = &local_operand_1; if (call_rescan_case) { /* Rescan for a call. The second operand of the call is the first operand of the selection. */ make_rescan_operands(rcblock, (an_operand *)NULL, operand_1, (an_operand *)NULL, &operator_position, &operator_tok_seq_number, (a_source_position *)NULL); /* The operand passed in is the second operand of the selection. */ copy_operand(result, &operand_2); } else { /* Normal case, not a rescan for a call. */ make_rescan_operands(rcblock, operand_1, &operand_2, (an_operand *)NULL, &operator_position, &operator_tok_seq_number, (a_source_position *)NULL); } /* if */ } else { /* Normal, non-rescan, processing. */ operator_token = curr_token; operator_position = pos_curr_token; operator_tok_seq_number = curr_token_sequence_number; } /* if */ /* Remember if this was an arrow or a dot selector. */ is_arrow_operator = (operator_token == tok_arrow_star); if (curr_expr_kind_is_traditional_const()) { if (curr_expr_kind_is(ek_pp)) { /* Operation not allowed in preprocessor expression. */ expr_pos_error(ec_bad_pp_operator, &operator_position); err = TRUE; } else if (curr_expr_kind_is(ek_integral_constant)) { /* Operation not allowed in integral constant expression. */ expr_pos_error(ec_bad_integral_operator, &operator_position); err = TRUE; } else if (curr_expr_kind_is(ek_template_arg)) { /* Operation not allowed in a template argument expression. */ expr_pos_error(ec_bad_templ_arg_expr_operator, &operator_position); err = TRUE; } else { /* Operation not allowed in constant expressions. */ expr_pos_error(ec_bad_constant_operator, &operator_position); err = TRUE; } /* if */ } /* if */ if (rcblock == NULL) { /* Scan the second operand. */ (void)get_token(); scan_expr(&operand_2, PREC_PTR_TO_MEMBER, EOPT_NO_OPTIONS); } /* if */ if (err) { /* Operator is not allowed in this kind of expression. */ make_error_operand(result); operand_will_not_be_used_because_of_error(operand_1); operand_will_not_be_used_because_of_error(&operand_2); } else if (!is_arrow_operator && is_template_dependent_context() && (is_template_dependent_type(operand_1->type) || is_template_dependent_type(operand_2.type))) { /* A cast to a dependent reference type spelled with a type operator, as in "static_cast(x)", leaves the reference type on the operand so that the type operator is not lost. Look through references so that such operands are checked on their referenced types. */ a_type_ptr operand_1_type = skip_reference_type(operand_1->type); a_type_ptr operand_2_type = skip_reference_type(operand_2.type); /* If either operand has a template parameter type, we cannot check the operand types completely. Just produce an expression with a generic operator. This is for ".*" only; the "->*" case is handled by check_for_operator_overloading. */ if (!(is_class_struct_union_type(operand_1_type) || could_be_dependent_class_type(operand_1_type))) { if (!is_error_type(operand_1_type)) { type_error_in_operand(ec_expr_not_class, operand_1, operand_1->type); } /* if */ make_error_operand(result); } else if (!(is_template_param_type(operand_2_type) || is_ptr_to_member_type(operand_2_type))) { if (!is_error_type(operand_2_type)) { error_in_operand(ec_expr_not_ptr_to_member, &operand_2); } /* if */ make_error_operand(result); } else { template_binary_operation((an_expr_operator_kind)eok_pm_field, operand_1, &operand_2, result, &operator_position, operator_tok_seq_number, (a_source_position *)NULL); } /* if */ processed = TRUE; } else { if (is_arrow_operator && /* Note -- not is_overloadable_type_first_operand on purpose. C++/CLI does not allow overloading of ->* in ref classes. */ (is_overloadable_type_operand(operand_1) || is_overloadable_type_operand(&operand_2))) { /* Look for C++ operator overloading cases ("->*" only). */ /* In standard C++, it is safe to assume that there is no predefined meaning if the operand is overloadable; however, this is not the case in C++/CX mode because handle types are overloadable and they have a predefined meaning in a ->* context. */ a_boolean has_predef_meaning = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED if (cppcx_enabled && is_handle_type(operand_1->type)) { has_predef_meaning = TRUE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ check_for_operator_overloading((an_opname_kind)onk_arrow_star, /*unary_operator=*/FALSE, /*must_be_member_function=*/FALSE, /*try_conversions=*/TRUE, has_predef_meaning, operand_1, &operand_2, &operator_position, operator_tok_seq_number, (a_nondependent_call_depth)0, (a_source_position *)NULL, result, &processed); } /* if */ if (!processed) { /* Non-operator-function cases. Combine the operands into the pointer-to-member selection. */ combine_ptr_to_member_operands(operand_1, &operand_2, is_arrow_operator, &operator_position, result, bound_function_selector); } /* if */ } /* if */ /* The position of the operand is the position of the selection except when the operand is a bound function, in which case it's the position of the second operand (because the selector has its own operand). */ if (result->bound_function) { set_operand_position(result, &operand_2.position, &operand_2.end_position, (a_source_position *)NULL); } else { /* Not a bound function; the operand position reflects the entire selection. */ set_operand_position(result, &operand_1->position, &operand_2.end_position, &operator_position); } /* if */ record_operator_position_in_rescan_info(result, &operator_position, operator_tok_seq_number, (a_source_position *)NULL); rule_out_expr_kinds(ROEK_CONSTANT, result); db_exit(); } /* scan_ptr_to_member_operator */ void rescan_selector_of_call(a_rescan_control_block *rcblock, an_operand *operand, an_operand *bound_function_selector) /* Rescan the selector expression of a call and return it in *bound_function_selector, also binding it to the function operand "operand". rcblock->expr is the expression for the call. Its selector expression is copied with substitution of template arguments, and then semantic analysis is redone on it and on the implied selection operator (e.g., "->") between the selector and the function operand. rcblock also gives context information for the template deduction being done, e.g., the template argument list being tried. */ { an_expr_node_ptr call_node = rcblock->expr; a_token_kind saved_operator_token = rcblock->operator_token; a_boolean pm_case = FALSE; check_assertion(is_operation_node(call_node)); /* Determine the type of implied selection operation. */ switch (call_node->variant.operation.kind) { case eok_dot_member_call: rcblock->operator_token = tok_period; break; case eok_points_to_member_call: rcblock->operator_token = tok_arrow; break; case eok_dot_pm_call: rcblock->operator_token = tok_period_star; pm_case = TRUE; break; case eok_points_to_pm_call: rcblock->operator_token = tok_arrow_star; pm_case = TRUE; break; default: unexpected_condition(); } /* switch */ if (pm_case) { /* ".*" and "->*" pointer-to-member operator cases. */ scan_ptr_to_member_operator((an_operand *)NULL, rcblock, /*call_rescan_case=*/TRUE, operand, bound_function_selector); } else { /* "." and "->" operator cases. */ /* It's easier to let the operator routine work out the second operand itself (it uses a locator form instead of an operand), so discard the operand already created. */ discard_operand(operand); scan_field_selection_operator((an_operand *)NULL, rcblock, /*call_rescan_case=*/TRUE, /*offsetof_case=*/FALSE, operand, bound_function_selector); } /* if */ rcblock->operator_token = saved_operator_token; } /* rescan_selector_of_call */ #if MICROSOFT_EXTENSIONS_ALLOWED static an_expr_node_ptr make_node_from_property_ref_operand( an_operand *operand) /* Extract an expression node from a property-ref operand and return it. This is used when the original copy of a cloned property-ref operand must be evaluated before the copies, e.g., to set one or more temporaries. The expression returned concatenates all the pieces that have side effects into one comma expression, which will then be inserted early in the evaluation sequence. The expression returned can be NULL if none of the expressions has side effects (usually not possible, but can happen for a C++/CLI static property reference). */ { an_expr_node_ptr expr; an_arg_list_elem_ptr alep; check_assertion(is_property_ref_operand(operand)); expr = operand->variant.property_ref.object; if (expr != NULL) { if (!node_has_side_effects(expr, (a_boolean *)NULL)) { /* Discard the object expression because it has no side effects. */ expr = NULL; } /* if */ } /* if */ /* Add any subscript operands. */ alep = operand->variant.property_ref.subscripts; for (; alep != NULL; alep = next_elem(alep)) { an_expr_node_ptr sub_expr; check_arg_list_elem_is_expression(alep); sub_expr = make_node_from_operand(operand_of_arg_list_elem(alep)); if (node_has_side_effects(sub_expr, (a_boolean *)NULL)) { if (expr == NULL) { expr = sub_expr; } else { expr = make_comma_node(expr, sub_expr); expr->compiler_generated = TRUE; } /* if */ } /* if */ } /* for */ return expr; } /* make_node_from_property_ref_operand */ static void clone_property_ref_operand(an_operand *operand, an_operand *operand_clone, an_expr_node_ptr *temp_init_expr) /* Clone an operand used in a Microsoft property reference. operand is the original operand, and operand_clone is set to a clone of that. If the cloning of the operand required setting a temporary, *temp_init_expr is set to the code that must be evaluated early to get the temporary initialized; otherwise, it is set to NULL. */ { a_boolean temp_init_used; check_assertion(is_property_ref_operand(operand)); *temp_init_expr = NULL; clone_operand(operand, operand_clone, /*vars_can_change=*/TRUE, &temp_init_used, /*treat_as_potential_prvalue=*/FALSE); if (temp_init_used) { /* The cloning required a temporary. Arrange for setting the temporary before any of the code that uses it (by adding a comma expression later). */ /* Extract and return the code that initializes the temporary. */ *temp_init_expr = make_node_from_property_ref_operand(operand); /* In some cases involving C++/CLI static properties, the temp_init_expr might be NULL here, because the original operand has no side effects and no temporaries are needed. */ if (*temp_init_expr != NULL) { /* Make another reuse of the expression for use in the "get" call. */ an_operand temp_operand; a_boolean dummy_temp_init_used; a_ref_entry_ptr saved_ref_entries = operand->ref_entries_list; operand->ref_entries_list = NULL; clone_operand(operand, &temp_operand, /*vars_can_change=*/TRUE, &dummy_temp_init_used, /*treat_as_potential_prvalue=*/FALSE); free_attachments_to_operand(operand); copy_operand(&temp_operand, operand); operand->ref_entries_list = saved_ref_entries; } /* if */ } /* if */ } /* clone_property_ref_operand */ static void process_property_ref_incr_decr( a_boolean is_increment, a_boolean is_post, a_boolean is_overloaded, a_source_position *operator_position, an_operand *operand, an_operand *operand_clone, an_operand *get_result_clone, an_expr_node_ptr temp_init_expr, an_operand *result); static void prepare_property_ref_incr_decr( a_boolean is_increment, a_boolean is_post, a_source_position *operator_position, a_token_sequence_number operator_tok_seq_number, an_operand *operand, an_operand *operand_clone, an_operand *get_result_clone, an_expr_node_ptr *temp_init_expr, an_operand *result, a_boolean *processed) /* Do the first part of processing for an increment or decrement of a reference to a member declared with the Microsoft property extension. is_increment is TRUE if the operation is an increment, FALSE for a decrement. is_post is TRUE for postfix, FALSE for prefix. operator_position gives the position of the "++" or "--" operator, and operator_tok_seq_number gives the token position of that operator. operand is the operand of the increment/decrement; it is transformed to an rvalue that is a call of the appropriate "get" routine. operand_clone is set to a clone of the original operand, for use later when generating the "put" call. For a postfix operator, *get_result_clone is set to a clone of the "get" function result, for use later as the result of the overall operation. Operator overloading is checked for, and if it applies, it is handled, the result is placed in *result, and *processed is set to TRUE. If the cloning of the operand required setting a temporary, *temp_init_expr is set to the code that must be evaluated early to get the temporary initialized; otherwise, it is set to NULL. */ { check_assertion(is_property_ref_operand(operand)); /* Make a clone of the operand, to be used in the store. */ clone_property_ref_operand(operand, operand_clone, temp_init_expr); /* Transform the operand 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 (is_post) { /* For postfix operators, save a copy of the result of the "get" function call for use later as the result of the overall operation. */ a_boolean temp_used; clone_operand(operand, get_result_clone, /*vars_can_change=*/TRUE, &temp_used, /*treat_as_potential_prvalue=*/TRUE); } /* if */ if (is_overloadable_type_first_operand(operand)) { /* Look for C++ operator overloading cases. */ an_operand gen_operand, *second_operand; a_constant_ptr gen_constant = local_constant(); a_boolean try_anachronism = FALSE; a_boolean has_predef_meaning = is_enum_type(operand->type); an_opname_kind kind; if (is_cppcli_property_or_event(operand_clone->symbol)) { /* For C++/CLI properties, use operator++ or operator--. */ kind = (an_opname_kind)(is_increment ? onk_plus_plus : onk_minus_minus); if (!is_post) { second_operand = NULL; } else { /* For postfix, the usual operator function is a two-argument function, with a zero passed for the second argument. */ set_integer_constant(gen_constant, (a_host_large_integer)0L, (an_integer_kind)ik_int); make_constant_operand(gen_constant, &gen_operand); second_operand = &gen_operand; /* If that function fails, we'll try the anachronism of using the one-argument function. */ try_anachronism = TRUE; } /* if */ } else { /* For declspec properties, use operator+ and operator-, with an implied "1" as the second operand. */ kind = (an_opname_kind)(is_increment ? onk_plus : onk_minus); set_integer_constant(gen_constant, (a_host_large_integer)1L, (an_integer_kind)ik_int); make_constant_operand(gen_constant, &gen_operand); second_operand = &gen_operand; } /* if */ check_for_operator_overloading(kind, /*unary_operator=*/(second_operand == NULL), /*must_be_member_function=*/FALSE, /*try_conversions=*/!try_anachronism, /*has_predef_meaning=*/has_predef_meaning || try_anachronism, operand, second_operand, operator_position, operator_tok_seq_number, (a_nondependent_call_depth)0, (a_source_position *)NULL, result, processed); if (!*processed && try_anachronism) { /* Try the one-argument function for postfix ++ or -- after failing to find a two-argument function. */ check_for_operator_overloading(kind, /*unary_operator=*/TRUE, /*must_be_member_function=*/FALSE, /*try_conversions=*/TRUE, /*has_predef_meaning=*/has_predef_meaning, operand, (an_operand *)NULL, operator_position, operator_tok_seq_number, (a_nondependent_call_depth)0, (a_source_position *)NULL, result, processed); } /* if */ if (*processed) { /* Overloading applied, so finish up the rewrite and return *processed = TRUE to the caller saying everything has been handled. */ process_property_ref_incr_decr(is_increment, is_post, /*is_overloaded=*/TRUE, operator_position, result, operand_clone, get_result_clone, *temp_init_expr, result); *temp_init_expr = NULL; } /* if */ release_local_constant(&gen_constant); } /* if */ } /* prepare_property_ref_incr_decr */ void insert_temporary_initialization(an_expr_node_ptr temp_init_expr, an_operand *result) /* If temp_init_expr is non-NULL, insert the temporary-initialization code it points to into result so it executes before whatever is originally in result. Mark the resulting eok_comma node as a node in a property reference rewrite. */ { if (temp_init_expr != NULL) { an_operand orig_operand; an_expr_node_ptr expr; orig_operand = *result; expr = make_node_from_operand(result); expr = make_comma_node(temp_init_expr, expr); expr->compiler_generated = TRUE; expr->variant.operation.rewritten_property_reference_kind = (a_rewritten_property_reference_kind)rprk_comma_discard_first; make_expression_operand(expr, result); if (is_a_glvalue(&orig_operand) || is_a_function_designator(&orig_operand)) { /* The original operand is a glvalue, so the result of the comma operator is a glvalue. */ set_glvalue_operand_state(result); } /* if */ restore_operand_details_incl_ref(result, &orig_operand); } /* if */ } /* insert_temporary_initialization */ static void process_property_ref_incr_decr( a_boolean is_increment, a_boolean is_post, a_boolean is_overloaded, a_source_position *operator_position, an_operand *operand, an_operand *operand_clone, an_operand *get_result_clone, an_expr_node_ptr temp_init_expr, an_operand *result) /* Generate the IL operation for an increment or decrement operation on a reference to a member declared with the Microsoft property extension. is_increment is TRUE if the operation is an increment, FALSE for a decrement. is_post is TRUE for postfix, FALSE for prefix. operator_position gives the source position of the operator. "operand" is the operand to be incremented/decremented, already transformed into a call of the appropriate "get" function. If is_overloaded is TRUE, the operation is overloaded, and operand is already the call of the overloaded operator+ or operator- (or operator++ or operator--) function on the "get" function result. operand_clone is a clone of the original operand, to be used in a call of the appropriate "put" function. For a postfix operator, *get_result_clone is a clone of the "get" function result, for use as the result of the overall operation. If temp_init_expr is non-NULL, the cloning of the operand required setting a temporary, and temp_init_expr points to the code to set the temporary, which must be inserted before the overall operation so it will be evaluated before any use of the temporary. The overall result is placed in *result. */ { an_operand operator_result; a_rewritten_property_reference_kind rprk = (is_post ? (a_rewritten_property_reference_kind)rprk_post_incr_decr : (a_rewritten_property_reference_kind)rprk_pre_incr_decr); if (!is_overloaded) { /* Add the code to add 1 to the value fetched by the "get" call. */ a_constant_ptr one_constant = local_constant(); an_operand one_operand; a_type_ptr result_type; an_expr_operator_kind op; /* Make a constant "1" of the right type. */ set_integer_constant(one_constant, (a_host_large_integer)1L, (an_integer_kind)ik_int); make_constant_operand(one_constant, &one_operand); /* Determine the result type. */ result_type = determine_arithmetic_conversions(operand, &one_operand); /* Change the type of the operands as needed (usually to the result type). */ op = which_binary_operator(is_increment ? tok_plus : tok_minus, result_type); do_operand_transformations(operand, TOPT_NO_OPTIONS); change_binary_operand_types(result_type, operand, &one_operand, op); /* Generate the IL for the operation. */ do_binary_operation(op, operand, &one_operand, result_type, result, operator_position, NO_TOKEN_SEQUENCE_NUMBER); release_local_constant(&one_constant); } /* if */ if (!is_post) { /* For a prefix operator, remember the result of the get()+1 computation so we can return that as the value of the operation. */ a_boolean temp_used; clone_operand(result, &operator_result, /*vars_can_change=*/TRUE, &temp_used, /*treat_as_potential_prvalue=*/TRUE); } else { /* For a postfix operator, *get_result_clone was previously set to the result of the "get" function call. */ copy_operand(get_result_clone, &operator_result); } /* if */ /* Add a call of the appropriate "put" routine. */ rewrite_property_reference(operand_clone, result, rprk, (a_routine_ptr *)NULL); copy_operand(operand_clone, result); /* Add code to fetch the previously-evaluated result of the operation. For prefix operators, that is the value of the get()+1 computation. For postfix operators, it is the value of the get call. */ { an_expr_node_ptr expr = make_node_from_operand(result); an_operand orig_operand; orig_operand = *operand; /* In standard C++, the prefix operators return an lvalue, the postfix operators an rvalue. However, with a property, there is no underlying lvalue, so we return an rvalue in all cases. */ if (is_an_lvalue(&operator_result)) { conv_glvalue_to_prvalue(&operator_result); } /* if */ expr = make_comma_node(expr, make_node_from_operand(&operator_result)); expr->compiler_generated = TRUE; expr->variant.operation.rewritten_property_reference_kind = (a_rewritten_property_reference_kind)rprk_comma_discard_second; make_expression_operand(expr, result); restore_operand_details(result, &orig_operand); } /* Insert temporary-initialization code if required. */ insert_temporary_initialization(temp_init_expr, result); } /* process_property_ref_incr_decr */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ static void scan_postfix_incr_decr(an_operand *operand, a_rescan_control_block *rcblock, an_operand *result) /* Scan the postfix increment ("++") and decrement ("--") operators. *operand is the operand. The current token is the operator. Make an expression for the operation, and return an operand for that in *result. If rcblock is non-NULL, redo semantic analysis on a previously-scanned expression, and return the result in *result (or an error indication in *rcblock). operand is expected to be NULL in that case. */ { an_expr_operator_kind op; a_token_kind operator_token; a_boolean is_increment; a_type_ptr result_type = NULL; a_boolean err = FALSE, processed = FALSE; an_operand local_operand, zero_operand; an_opname_kind opname_kind; a_source_position operator_position; a_token_sequence_number operator_tok_seq_number; a_boolean property_ref_case = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED an_operand operand_clone, get_result_clone; a_boolean operand_clone_unused = FALSE; an_expr_node_ptr temp_init_expr = NULL; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ db_enter(4, "scan_postfix_incr_decr"); if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. */ operator_token = rcblock->operator_token; check_assertion(operand == NULL); operand = &local_operand; make_rescan_operands(rcblock, operand, (an_operand *)NULL, (an_operand *)NULL, &operator_position, &operator_tok_seq_number, (a_source_position *)NULL); #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = rcblock->expr->expr_range.end; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } else { /* Normal, non-rescan, processing. */ operator_token = curr_token; operator_position = pos_curr_token; operator_tok_seq_number = curr_token_sequence_number; #if EXTRA_SOURCE_POSITIONS_IN_IL /* Save the end position of the operator. */ end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* if */ is_increment = (operator_token == tok_plus_plus); if (curr_expr_kind_is_traditional_const()) { /* Postfix ++/-- not allowed in constant expressions. */ expr_pos_error(ec_bad_constant_operator, &operator_position); make_error_operand(result); operand_will_not_be_used_because_of_error(operand); } else { #if MICROSOFT_EXTENSIONS_ALLOWED property_ref_case = is_property_ref_operand(operand); if (property_ref_case) { /* The operand is a reference to a member declared as a Microsoft property. The fetch of the member will be made via a call of a "get" function, and the store will be made via a call of a "put" function. */ prepare_property_ref_incr_decr(is_increment, /*is_post=*/TRUE, &operator_position, operator_tok_seq_number, operand, &operand_clone, &get_result_clone, &temp_init_expr, result, &processed); operand_clone_unused = TRUE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (C_dialect == C_dialect_cplusplus && !property_ref_case && is_overloadable_type_first_operand(operand)) { a_boolean allow_one_arg = (allow_anachronisms || ms_version_is(<1900)); a_boolean has_predef_meaning = is_enum_type(operand->type); /* Look for C++ operator overloading cases. */ /* Note that postfix ++/-- use a two-argument function to distinguish them from the prefix ++/--, which use a one-argument function. See ARM 13.4.7. The second compiler-supplied argument is an integer zero. Earlier versions of Cfront used the single-argument operator for postfix ++/--, and earlier versions of Microsoft's compiler supported that also (indicated by the variable allow_one_arg above). */ make_integer_constant_operand(&zero_operand, (a_host_large_integer)0L); #if EXTRA_SOURCE_POSITIONS_IN_IL zero_operand.end_position = end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ opname_kind = opname_kind_for_token[(int)operator_token]; check_for_operator_overloading(opname_kind, /*unary_operator=*/FALSE, /* sic! */ /*must_be_member_function=*/FALSE, /*try_conversions=*/!allow_one_arg, /*has_predef_meaning=*/ has_predef_meaning || allow_one_arg, operand, &zero_operand, &operator_position, operator_tok_seq_number, (a_nondependent_call_depth)0, (a_source_position *)NULL, result, &processed); if (!processed && allow_one_arg) { /* Try the anachronism that allows a one-argument function to be used for both prefix and postfix ++/--. */ check_for_operator_overloading(opname_kind, /*unary_operator=*/TRUE, /*must_be_member_function=*/FALSE, /*try_conversions=*/FALSE, /*has_predef_meaning=*/TRUE, operand, (an_operand *)NULL, &operator_position, operator_tok_seq_number, (a_nondependent_call_depth)0, (a_source_position *)NULL, result, &processed); if (processed) { if (!is_error_operand(result)) { an_error_severity sev = allow_anachronisms ? anachronism_error_severity : es_warning; expr_pos_st_diagnostic(sev, ec_single_arg_postfix_incr_decr_anachronism, &operator_position, token_names[(int)operator_token]); } /* if */ } else { /* The anachronism does not apply, so try finding a conversion function that will convert the operand to the right type for the builtin version of the operator. Note that this call will also try the normal match again, and fail. */ check_for_operator_overloading(opname_kind, /*unary_operator=*/FALSE, /* sic! */ /*must_be_member_function=*/FALSE, /*try_conversions=*/TRUE, has_predef_meaning, operand, &zero_operand, &operator_position, operator_tok_seq_number, (a_nondependent_call_depth)0, (a_source_position *)NULL, result, &processed); } /* if */ } /* if */ } /* if */ if (!processed && !C_mode() && is_volatile_qualified_type(operand->type)) { /* P1152R4 (in C++20) deprecated using a volatile-qualified operand in a postfix increment/decrement expression. */ an_error_severity sev = cpp20_mode ? es_warning : es_remark; expr_pos_diagnostic(sev, is_increment ? ec_volatile_inc_deprecated : ec_volatile_dec_deprecated, &operand->position); } /* if */ if (!processed && operator_not_allowed_in_cpp11_constant_expr(&operator_position)) { /* Postfix ++/-- not allowed in C++11 constant expressions. Note this is checked after we've looked for the possibility of the operator being overloaded. */ make_error_operand(result); } else if (!processed) { /* Non-operator-function cases. */ do_operand_transformations(operand, TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION); /* The lvalue must be for a scalar, and if it is a pointer, it must be a pointer to an object. */ if (!check_scalar_operand(operand)) { /* Operand is not scalar. */ err = TRUE; } else if (is_nullptr_type(operand->type)) { /* Increment/decrement not permitted on std::nullptr_t. */ error_in_operand(ec_expr_not_arithmetic_or_unscoped_enum, operand); err = TRUE; } else { if (is_pointer_type(operand->type)) { if (gcc_mode && (is_void_type(type_pointed_to(operand->type)) || is_function_type(type_pointed_to(operand->type)))) { /* In some versions of GNU C void and function pointers can be incremented and decremented. */ if (!expr_stack->marked_as_gnu_extension) { expr_pos_warning(ec_nonobject_pointer_arithmetic, &operator_position); } /* if */ } else if (!check_object_pointer_operand( operand, ec_expr_not_pointer_to_object)) { err = TRUE; } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED } else if (property_ref_case) { /* No further checking here. */ } else if (cli_or_cx_enabled && is_scoped_enum_type(operand->type)) { /* C++/CLI, unlike standard C++11, defines built-in ++ and -- for scoped enumeration types. */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else if (C_dialect == C_dialect_cplusplus && is_enum_type(operand->type)) { /* Enum types are not allowed (except for scoped enums in C++/CLI mode). An unscoped enum promotes to integer for the operation, and then can't get back to enum. C++11 scoped enumerations don't promote, but the standard doesn't define built-in increment or decrement operations for them. */ if (allow_anachronisms) { expr_pos_diagnostic(anachronism_error_severity, ec_mixed_enum_type_anachronism, &operand->position); } else { expr_pos_error(ec_enum_type_not_allowed, &operator_position); err = TRUE; } /* if */ } else if (!C_mode() && is_bool_type(operand->type)) { /* "++" on bool in C++ is allowed in modes earlier than C++17, but deprecated. "--" on bool is not allowed. */ if (is_increment) { if (operator_bool_increment_allowed) { expr_pos_warning(ec_incr_of_bool_deprecated, &operand->position); } else { error_in_operand(ec_incr_of_bool_not_allowed, operand); } /* if */ } else { error_in_operand(ec_bool_type_not_allowed, operand); } /* if */ #if C99_IL_EXTENSIONS_SUPPORTED } else if (is_nonreal_floating_type(operand->type)) { if (gnu_mode && is_complex_type(operand->type)) { /* GNU allows increment/decrement of complex types in both C and C++ modes. */ } else { /* Complex and imaginary operands are not allowed. */ error_in_operand(ec_complex_type_not_allowed, operand); } /* if */ #endif /* C99_IL_EXTENSIONS_SUPPORTED */ } /* if */ } /* if */ if (err) { /* Some error already. */ #if MICROSOFT_EXTENSIONS_ALLOWED } else if (property_ref_case) { /* No further checking here. */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else if (!check_modifiable_lvalue_operand(operand)) { /* Operand is not a modifiable lvalue. */ err = TRUE; } else { /* Operand is okay. */ modifying_lvalue(operand, /*value_used=*/TRUE); result_type = prvalue_type(operand->type); } /* if */ if (err) { /* Error of some kind. */ make_error_operand(result); #if MICROSOFT_EXTENSIONS_ALLOWED } else if (property_ref_case) { /* Operand is a reference to a Microsoft property. */ process_property_ref_incr_decr(is_increment, /*is_post=*/TRUE, /*is_overloaded=*/FALSE, &operator_position, operand, &operand_clone, &get_result_clone, temp_init_expr, result); operand_clone_unused = FALSE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else { /* Determine the IL operator to use. */ a_type_ptr op_type = skip_typerefs(result_type); op = is_increment ? (an_expr_operator_kind)eok_post_incr : (an_expr_operator_kind)eok_post_decr; switch (op_type->kind) { case tk_integer: case tk_float: case tk_pointer: #if C99_IL_EXTENSIONS_SUPPORTED case tk_complex: #endif /* C99_IL_EXTENSIONS_SUPPORTED */ /* Nothing more to check. */ break; #if FIXED_POINT_ALLOWED case tk_fixed_point: if (op_type->variant.fixed_point.is_fract_type) { /* _Fract types can only represent values between -1 and 1: adding or subtracting one is rarely intentional in that domain. */ expr_pos_warning(ec_operation_may_not_fit_in_fixed_point_result, &operator_position); } /* if */ break; #endif /* FIXED_POINT_ALLOWED */ default: unexpected_condition_str("scan_postfix_incr_decr: bad type"); } /* switch */ build_unary_result_operand(operand, op, result_type, result); } /* if */ } /* if */ } /* if */ if (rcblock == NULL) { /* Get past the "++" or "--". */ (void)get_token(); } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (operand_clone_unused) { operand_will_not_be_used_because_of_error(&operand_clone); operand_will_not_be_used_because_of_error(&get_result_clone); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ set_operand_position(result, &operand->position, &end_position, &operator_position); record_operator_position_in_rescan_info(result, &operator_position, operator_tok_seq_number, (a_source_position *)NULL); rule_out_expr_kinds(ROEK_CONSTANT, result); db_exit(); } /* scan_postfix_incr_decr */ static void change_assignment_result_to_lvalue(an_operand *result, an_operand *lvalue_operand, a_type_ptr result_type) /* In C++ mode, assignment operators and prefix ++/-- return lvalues. Change the operation in *result from a prvalue-returning operation to an lvalue-returning operation. *lvalue_operand is the operand for the lvalue being operated upon. result_type is the original type of the first operand, which may differ from the current type of the result in having type qualifiers. This routine is called only in C++ mode. */ { an_expr_node_ptr node; if (is_error_operand(result)) { normalize_error_operand(result); } else { check_assertion(is_expression_operand(result)); node = result->variant.expression; node->is_lvalue = TRUE; node->variant.operation.returns_lvalue_instead_of_usual_rvalue = TRUE; node->type = result->type = result_type; /* Keep the reference entries from the lvalue operand. */ result->ref_entries_list = lvalue_operand->ref_entries_list; set_glvalue_operand_state(result); } /* if */ } /* change_assignment_result_to_lvalue */ static void scan_prefix_incr_decr(a_rescan_control_block *rcblock, an_operand *result) /* Scan the prefix increment ("++") and decrement ("--") operators. The current token is the operator. Scan the operand, build an expression, and return an operand for that in *result. If rcblock is non-NULL, redo semantic analysis on a previously-scanned expression, and return the result in *result (or an error indication in *rcblock). */ { a_token_kind operator_token; an_operand operand; a_source_position operator_position; a_token_sequence_number operator_tok_seq_number; an_expr_operator_kind op; a_boolean is_increment; a_type_ptr orig_result_type = NULL, result_type = NULL; a_boolean err = FALSE, processed = FALSE; a_boolean property_ref_case = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED an_operand operand_clone; a_boolean operand_clone_unused = FALSE; an_expr_node_ptr temp_init_expr = NULL; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ db_enter(4, "scan_prefix_incr_decr"); if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. */ operator_token = rcblock->operator_token; make_rescan_operands(rcblock, &operand, (an_operand *)NULL, (an_operand *)NULL, &operator_position, &operator_tok_seq_number, (a_source_position *)NULL); } else { /* Normal, non-rescan, processing. */ operator_token = curr_token; operator_position = pos_curr_token; operator_tok_seq_number = curr_token_sequence_number; } /* if */ is_increment = (operator_token == tok_plus_plus); if (curr_expr_kind_is_traditional_const()) { /* Prefix ++ and -- are not allowed in constant expressions. */ expr_pos_error(ec_bad_constant_operator, &operator_position); err = TRUE; } /* if */ if (rcblock == NULL) { /* Scan the operand. */ (void)get_token(); scan_expr(&operand, PREC_PREFIX, EOPT_PRESERVE_PROPERTY_REF); } /* if */ if (err) { /* Operator not allowed in this kind of expression. */ make_error_operand(result); operand_will_not_be_used_because_of_error(&operand); } else { #if MICROSOFT_EXTENSIONS_ALLOWED property_ref_case = is_property_ref_operand(&operand); if (property_ref_case && !err) { /* The operand is a reference to a member declared as a Microsoft property. The fetch of the member will be made via a call of a "get" function, and the store will be made via a call of a "put" function. */ prepare_property_ref_incr_decr(is_increment, /*is_post=*/FALSE, &operator_position, operator_tok_seq_number, &operand, &operand_clone, (an_operand *)NULL, &temp_init_expr, result, &processed); operand_clone_unused = TRUE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (C_dialect == C_dialect_cplusplus && !property_ref_case && is_overloadable_type_first_operand(&operand)) { /* Look for C++ operator overloading cases. */ check_for_operator_overloading(opname_kind_for_token[ (int)operator_token], /*unary_operator=*/TRUE, /*must_be_member_function=*/FALSE, /*try_conversions=*/TRUE, /*has_predef_meaning=*/ is_enum_type(operand.type), &operand, (an_operand *)NULL, &operator_position, operator_tok_seq_number, (a_nondependent_call_depth)0, (a_source_position *)NULL, result, &processed); } /* if */ if (!processed && !C_mode() && is_volatile_qualified_type(operand.type)) { /* P1152R4 (in C++20) deprecated using a volatile-qualified operand in a prefix increment/decrement expression. */ an_error_severity sev = cpp20_mode ? es_warning : es_remark; expr_pos_diagnostic(sev, is_increment ? ec_volatile_inc_deprecated : ec_volatile_dec_deprecated, &operand.position); } /* if */ if (!processed && operator_not_allowed_in_cpp11_constant_expr(&operator_position)) { /* Prefix ++/-- not allowed in C++11 constant expressions. Note this is checked after we've looked for the possibility of the operator being overloaded. */ make_error_operand(result); } else if (!processed) { /* Non-operator-function cases. */ do_operand_transformations(&operand, TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION); /* The lvalue must be for a scalar, and if it is a pointer, it must be a pointer to an object. */ if (!check_scalar_operand(&operand)) { /* Operand is not scalar. */ err = TRUE; } else if (is_nullptr_type(operand.type)) { /* Increment/decrement not permitted on std::nullptr_t. */ error_in_operand(ec_expr_not_arithmetic_or_unscoped_enum, &operand); err = TRUE; } else { if (is_pointer_type(operand.type)) { if (gcc_mode && (is_void_type(type_pointed_to(operand.type)) || is_function_type(type_pointed_to(operand.type)))) { /* GNU C allows void and function pointers to be incremented and decremented. */ if (!expr_stack->marked_as_gnu_extension) { expr_pos_warning(ec_nonobject_pointer_arithmetic, &operator_position); } /* if */ } else if (!check_object_pointer_operand( &operand, ec_expr_not_pointer_to_object)) { err = TRUE; } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED } else if (property_ref_case) { /* No further checking here. */ } else if (cli_or_cx_enabled && is_scoped_enum_type(operand.type)) { /* C++/CLI, unlike standard C++11, defines built-in ++ and -- for scoped enumeration types. */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else if (C_dialect == C_dialect_cplusplus && is_enum_type(operand.type)) { /* Enum types are not allowed (except for scoped enums in C++/CLI mode). An unscoped enum promotes to integer for the operation, and then can't get back to enum. C++11 scoped enumerations don't promote, but the standard doesn't define built-in increment or decrement operations for them. */ if (allow_anachronisms) { expr_pos_diagnostic(anachronism_error_severity, ec_mixed_enum_type_anachronism, &operand.position); } else { expr_pos_error(ec_enum_type_not_allowed, &operator_position); err = TRUE; } /* if */ } else if (!C_mode() && is_bool_type(operand.type)) { /* "++" on bool in C++ is allowed in modes earlier than C++17, but deprecated. "--" on bool is not allowed. */ if (is_increment) { if (operator_bool_increment_allowed) { expr_pos_warning(ec_incr_of_bool_deprecated, &operand.position); } else { error_in_operand(ec_incr_of_bool_not_allowed, &operand); } /* if */ } else { error_in_operand(ec_bool_type_not_allowed, &operand); } /* if */ #if C99_IL_EXTENSIONS_SUPPORTED } else if (is_nonreal_floating_type(operand.type)) { if (gnu_mode && is_complex_type(operand.type)) { /* GNU allows increment/decrement of complex types in both C and C++ modes. */ } else { /* Complex and imaginary operands are not allowed. */ error_in_operand(ec_complex_type_not_allowed, &operand); } /* if */ #endif /* C99_IL_EXTENSIONS_SUPPORTED */ } /* if */ } /* if */ if (err) { /* Some error already. */ #if MICROSOFT_EXTENSIONS_ALLOWED } else if (property_ref_case) { /* No further checking here. */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else if ((gpp_version_is(any_version) || ms_version_is(any_version)) && is_prototype_instantiation_context() && !is_const_qualified_type(operand.type)) { /* GCC and MSVC accept cases such as the following: template struct S { void f() { ++this; } }; */ prep_generic_operand_full(&operand, /*lvalue_expected=*/TRUE, /*rvalue_expected=*/FALSE); orig_result_type = operand.type; result_type = prvalue_type(orig_result_type); } else if (!check_modifiable_lvalue_operand(&operand)) { /* Operand is not a modifiable lvalue. */ err = TRUE; } else { /* Operand is okay. */ modifying_lvalue(&operand, /*value_used=*/TRUE); orig_result_type = operand.type; result_type = prvalue_type(orig_result_type); } /* if */ if (err) { /* Error of some kind. */ make_error_operand(result); #if MICROSOFT_EXTENSIONS_ALLOWED } else if (property_ref_case) { /* Operand is a reference to a Microsoft property. */ process_property_ref_incr_decr(is_increment, /*is_post=*/FALSE, /*is_overloaded=*/FALSE, &operator_position, &operand, &operand_clone, (an_operand *)NULL, temp_init_expr, result); operand_clone_unused = FALSE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } else { /* Determine the IL operator to use. */ a_type_ptr op_type = skip_typerefs(result_type); op = is_increment ? (an_expr_operator_kind)eok_pre_incr : (an_expr_operator_kind)eok_pre_decr; switch (op_type->kind) { case tk_integer: case tk_float: case tk_pointer: #if C99_IL_EXTENSIONS_SUPPORTED case tk_complex: #endif /* C99_IL_EXTENSIONS_SUPPORTED */ /* Nothing more to check. */ break; #if FIXED_POINT_ALLOWED case tk_fixed_point: if (op_type->variant.fixed_point.is_fract_type) { /* _Fract types can only represent values between -1 and 1: adding or subtracting one is rarely intentional in that domain. */ expr_pos_warning(ec_operation_may_not_fit_in_fixed_point_result, &operator_position); } /* if */ break; #endif /* FIXED_POINT_ALLOWED */ default: unexpected_condition_str("scan_prefix_incr_decr: bad type"); } /* switch */ build_unary_result_operand(&operand, op, result_type, result); /* In C++, the prefix ++ and -- operators return lvalues. */ if (C_dialect == C_dialect_cplusplus) { change_assignment_result_to_lvalue(result, &operand, orig_result_type); } /* if */ } /* if */ } /* if */ } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED if (operand_clone_unused) { operand_will_not_be_used_because_of_error(&operand_clone); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ set_operand_position(result, &operator_position, &operand.end_position, &operator_position); record_operator_position_in_rescan_info(result, &operator_position, operator_tok_seq_number, (a_source_position *)NULL); rule_out_expr_kinds(ROEK_CONSTANT, result); db_exit(); } /* scan_prefix_incr_decr */ static void scan_ampersand_operator(a_rescan_control_block *rcblock, an_operand *result, a_local_expr_options_set local_options) /* Scan the "&" (address of) operator. The current token is the operator. Scan the operand, build an expression, and return an operand for that in *result. If rcblock is non-NULL, redo semantic analysis on a previously-scanned expression, and return the result in *result (or an error indication in *rcblock). */ { an_operand operand; a_source_position start_position, operator_position; a_token_sequence_number operator_tok_seq_number; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ a_boolean err = FALSE, processed = FALSE; an_expr_node_ptr expr; a_boolean is_address_of_ellipsis; db_enter(4, "scan_ampersand_operator"); if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. */ check_assertion(rcblock->operator_token == tok_ampersand); make_rescan_operands(rcblock, &operand, (an_operand *)NULL, (an_operand *)NULL, &operator_position, &operator_tok_seq_number, (a_source_position *)NULL); } else { /* Normal, non-rescan, processing. */ operator_position = pos_curr_token; operator_tok_seq_number = curr_token_sequence_number; } /* if */ start_position = operator_position; if (curr_expr_kind_is_traditional_const()) { if (curr_expr_kind_is(ek_pp)) { /* Address constants not allowed in preprocessing expressions. */ expr_pos_error(ec_bad_pp_operator, &start_position); err = TRUE; } else if (curr_expr_kind_is(ek_integral_constant)) { /* Address constants not allowed in integral constant expressions. */ expr_pos_error(ec_bad_integral_operator, &start_position); err = TRUE; } /* if */ } /* if */ if (rcblock == NULL) { /* Advance past the "&". */ (void)get_token(); is_address_of_ellipsis = (address_of_ellipsis_allowed && curr_token == (a_token_kind)tok_ellipsis); } else { /* Redoing semantic analysis. */ /* Address of ellipsis is not possible, because that construct can be used only within the body of a function, not in its header. */ is_address_of_ellipsis = FALSE; } /* if */ if (is_address_of_ellipsis) { /* Allow the &... extension, used in stdarg.h macros to get the address of the ellipsis arguments. */ if (err) { make_error_operand(result); } else if (depth_innermost_function_scope == NO_SCOPE_DEPTH || !f_skip_typerefs(current_routine_entry()->type)-> variant.routine.extra_info->has_ellipsis) { /* "&..." used outside a function, or in a function that does not have an ellipsis. */ expr_pos_error(ec_bad_address_of_ellipsis, &pos_curr_token); err = TRUE; make_error_operand(result); } else { a_type_ptr void_star_tp = make_pointer_type(void_type()); an_expr_node_ptr node = alloc_expr_node((an_expr_node_kind)enk_address_of_ellipsis); node->type = void_star_tp; make_expression_operand(node, result); if (strict_ansi_mode) { expr_pos_diagnostic(strict_ansi_error_severity, ec_nonstd_address_of_ellipsis, &pos_curr_token); } /* if */ } /* if */ #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* Advance past the "...". */ (void)get_token(); } else { /* Normal case, not the &... extension. */ a_boolean address_of_id_expression; if (rcblock == NULL) { /* Scan the operand. */ an_operand bound_function_selector, *p_bf_selector = NULL; a_local_expr_options_set options = local_options | EOPT_OPERAND_OF_ADDRESS_OF | EOPT_PTR_TO_MEMBER_CONTEXT; if (microsoft_mode && curr_expr_kind_is(ek_sizeof) && !expr_stack->is_type_operator_arg_expression) { options |= EOPT_ALLOW_BOUND_FUNCTION; p_bf_selector = &bound_function_selector; } /* if */ scan_expr_full(&operand, p_bf_selector, PREC_PREFIX, options); if (operand.bound_function) { combine_unneeded_selector_with_operand( &bound_function_selector, bound_function_selector.selector_is_object_pointer, &operand); } /* if */ } /* if */ address_of_id_expression = operand.is_id_expression; if (err) { /* Operator is not allowed in this kind of expression. */ make_error_operand(result); operand_will_not_be_used_because_of_error(&operand); } else { if (C_dialect == C_dialect_cplusplus && is_overloadable_type_first_operand(&operand) && !is_sym_for_member_operand(&operand)) { /* Look for C++ operator overloading cases. */ check_for_operator_overloading((an_opname_kind)onk_ampersand, /*unary_operator=*/TRUE, /*must_be_member_function=*/FALSE, /*try_conversions=*/FALSE, /*has_predef_meaning=*/TRUE, &operand, (an_operand *)NULL, &start_position, operator_tok_seq_number, (a_nondependent_call_depth)0, (a_source_position *)NULL, result, &processed); } /* if */ if (!processed) { a_boolean was_prvalue = is_a_prvalue(&operand); an_operand orig_operand = operand; /* Non-operator-function cases. */ /* As of this writing, this call suppresses every known transformation, but it's here to allow for future transformations. */ do_operand_transformations(&operand, TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION | TOPT_SUPPRESS_ARRAY_TO_POINTER_CONVERSION | TOPT_SUPPRESS_FUNCTION_TO_POINTER_CONVERSION | TOPT_SUPPRESS_MEMBER_FUNC_TO_PM_CONVERSION | TOPT_SUPPRESS_CHECK_FOR_INDEFINITE_FUNCTION); if (gnu_mode) { /* Get an lvalue back from what is ordinarily an rvalue in some cases in gnu mode. */ revert_gcc_rvalue_to_lvalue_if_possible(&operand, /*ignore_casts=*/FALSE); } else if (microsoft_mode && !C_mode()) { /* Likewise in Microsoft mode. */ revert_microsoft_rvalue_to_lvalue_if_possible(&operand); } /* if */ if ((c99_mode || gcc_mode) && is_expression_operand(&operand) && (expr = skip_parens(operand.variant.expression), is_operation_node(expr)) && node_operator_is(expr, eok_indirect)) { /* In C99 (see C99 standard section 6.5.3.2) and GNU C modes, a "&" and a "*" operator cancel out, e.g., "&*x" is just "x". This is particularly significant when x is a pointer to void, in that it avoids an error (note that *x is an rvalue in that case). */ expr = expr->variant.operation.operands; make_expression_operand(expr, result); #if EXTRA_SOURCE_POSITIONS_IN_IL /* Avoid changing the operator position in the expression when we set the operand position below. */ operator_position = expr->position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } else if (is_an_lvalue(&operand) || (microsoft_bugs && is_an_xvalue(&operand))) { if (C_dialect == C_dialect_pcc && is_array_type(operand.type)) { /* In pcc mode "&array" is the same as "array" implicitly converted to a pointer. It has type "pointer-to-array-element" rather than "pointer to array" as in ANSI. */ expr_pos_warning(ec_pcc_address_of_array, &start_position); conv_array_operand_to_pointer_operand(&operand); } else if (microsoft_bugs && microsoft_version < 1400 && operand_is_string_literal(&operand)) { /* Early Microsoft compilers ignore "&" in front of a string literal (wide or narrow, in parentheses or not) in both C and C++ mode. Note that the type of &"abc" is supposed to be a pointer to array, whereas "abc" decays to pointer to char. */ conv_array_operand_to_pointer_operand(&operand); } else { if (!C_mode() && was_prvalue && is_class_struct_union_type(operand.type)) { /* Warn on taking the address of a temporary. */ expr_pos_warning(ec_taking_address_of_temporary, &start_position); #if MICROSOFT_EXTENSIONS_ALLOWED } else if (cli_or_cx_enabled && is_ref_class_type(operand.type)) { /* C++/CLI doesn't allow "&" to apply to an object with a ref class type, on the theory that "%" ought to be used instead to create a handle. */ expr_pos_error(ec_addr_of_ref_class, &start_position); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ /* Convert the lvalue operand to a prvalue operand for the pointer. */ take_address_of_lvalue(&operand, &operator_position); } /* if */ /* Note that the copy preserves ref_entries_list. */ copy_operand(&operand, result); restore_operand_form_of_name_reference(result, &orig_operand); } else if (is_a_function_designator(&operand)) { /* "&" of a function designator. Change it to a pointer to the function. This includes overloaded functions. */ conv_function_designator_to_ptr_to_function(&operand, &operator_position, /*allow_ctor=*/FALSE, /*will_call=*/FALSE); /* Note that the copy preserves ref_entries_list. */ copy_operand(&operand, result); restore_operand_form_of_name_reference(result, &orig_operand); } else if (is_sym_for_member_operand(&operand)) { /* The operand is the name of a nonstatic data member, so the "&" operator returns a pointer-to-member. */ conv_sym_for_member_operand_to_ptr_to_member(&operand, &operator_position); copy_operand(&operand, result); restore_operand_form_of_name_reference(result, &orig_operand); } else 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); take_address_of_lvalue(&operand, &operator_position); copy_operand(&operand, result); restore_operand_form_of_name_reference(result, &orig_operand); } else { /* "&" applied to something that is not an lvalue or a function designator or another permitted case. */ if (!is_error_operand(&operand)) { error_in_operand(ec_expr_not_an_lvalue_or_function_designator, &operand); } /* if */ make_error_operand(result); } /* if */ if (is_constant_operand(result) && constant_is(&result->variant.constant, ck_ptr_to_member)) { #if MICROSOFT_EXTENSIONS_ALLOWED result->variant.constant.ptr_to_mem_constant_construct = TRUE; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ if (rcblock == NULL && record_name_references_in_context()) { a_constant_ptr constant = &result->variant.constant; if (result->name_reference_set) { a_name_reference_ptr nrp = alloc_name_reference(); *nrp = result->name_reference; check_assertion(constant->source_corresp.name_references ==NULL); constant->source_corresp.name_references = nrp; constant->variant.ptr_to_member.name_reference = nrp; } /* if */ } /* if */ } /* if */ } /* if */ if (address_of_id_expression) { result->is_address_of_id_expression = TRUE; } /* if */ check_assertion(!result->is_id_expression); } /* if */ #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = operand.end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* if */ set_operand_position(result, &start_position, &end_position, &operator_position); record_operator_position_in_rescan_info(result, &operator_position, operator_tok_seq_number, (a_source_position *)NULL); rule_out_expr_kinds(ROEK_INTEGRAL_CONSTANT, result); db_exit(); } /* scan_ampersand_operator */ #if MICROSOFT_EXTENSIONS_ALLOWED static void scan_handle_address_operator(a_rescan_control_block *rcblock, an_operand *result) /* Scan the C++/CLI unary "%" (handle address of) operator. The current token is the operator. Scan the operand, build an expression, and return an operand for that in *result. If rcblock is non-NULL, redo semantic analysis on a previously-scanned expression, and return the result in *result (or an error indication in *rcblock). */ { an_operand operand; an_expr_node_ptr expr; a_source_position start_position, operator_position; a_token_sequence_number operator_tok_seq_number; a_boolean err = FALSE; a_boolean template_case = FALSE; db_enter(4, "scan_handle_address_operator"); check_assertion(cli_or_cx_enabled); if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. */ check_assertion(rcblock->operator_token == tok_remainder); make_rescan_operands(rcblock, &operand, (an_operand *)NULL, (an_operand *)NULL, &operator_position, &operator_tok_seq_number, (a_source_position *)NULL); } else { /* Normal, non-rescan, processing. */ operator_position = pos_curr_token; operator_tok_seq_number = curr_token_sequence_number; } /* if */ start_position = operator_position; if (curr_expr_kind_is(ek_pp)) { /* Handles not allowed in preprocessing expressions. */ expr_pos_error(ec_bad_pp_operator, &start_position); err = TRUE; } else if (curr_expr_kind_is_traditional_const()) { /* Handles not allowed in constant expressions. */ expr_pos_error(ec_bad_constant_operator, &start_position); err = TRUE; } else if (operator_not_allowed_in_cpp11_constant_expr(&start_position)) { /* Handles not allowed in C++11 constant expressions. */ err = TRUE; } /* if */ if (rcblock == NULL) { /* Advance past the "%". */ (void)get_token(); /* Scan the operand. */ scan_expr(&operand, PREC_PREFIX, EOPT_NO_OPTIONS); } /* if */ if (err) { /* Operator is not allowed in this kind of expression. */ make_error_operand(result); operand_will_not_be_used_because_of_error(&operand); } else { /* As of this writing, this call suppresses every known transformation, but it's here to allow for future transformations. */ do_operand_transformations(&operand, TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION | TOPT_SUPPRESS_ARRAY_TO_POINTER_CONVERSION | TOPT_SUPPRESS_FUNCTION_TO_POINTER_CONVERSION | TOPT_SUPPRESS_MEMBER_FUNC_TO_PM_CONVERSION | TOPT_SUPPRESS_CHECK_FOR_INDEFINITE_FUNCTION); if (is_template_param_type(operand.type)) template_case = TRUE; if (is_error_operand(&operand)) { /* Leave an error operand mostly alone. Mark the address as being taken to prevent cascading diagnostics. */ change_ref_kinds(operand.ref_entries_list, SRK_ADDRESS_TAKEN); operand_will_not_be_used_because_of_error(&operand); make_error_operand(result); } else if (is_value_class_type(operand.type)) { /* A value class type object gets boxed. The operand need not be an lvalue in that case, since it gets copied. Any cv-qualifiers on the source object are dropped. */ do_operand_transformations(&operand, TOPT_NO_OPTIONS); expr = make_node_from_operand(&operand); expr = add_box_to_expression(expr, /*is_implicit=*/FALSE, /*handle_to_form=*/TRUE); make_expression_operand(expr, result); } else if (!is_cli_ref_or_interface_class_type(operand.type) && !template_case) { /* The operand of "%" must have a ref or interface class type. */ error_in_operand(ec_handle_of_non_managed, &operand); make_error_operand(result); } else { /* Make an eok_handle_to node for the operation. Note that a handle is never considered constant. Also note that the operand can be an rvalue. */ if (template_case) { prep_generic_operand(&operand); } /* if */ expr = make_node_from_operand(&operand); change_ref_kinds(operand.ref_entries_list, SRK_ADDRESS_TAKEN); expr = make_operator_node((an_expr_operator_kind)eok_handle_to, make_handle_type(expr->type), expr); make_expression_operand(expr, result); } /* if */ } /* if */ set_operand_position(result, &start_position, &operand.end_position, &operator_position); record_operator_position_in_rescan_info(result, &operator_position, operator_tok_seq_number, (a_source_position *)NULL); rule_out_expr_kinds(ROEK_CONSTANT, result); db_exit(); } /* scan_handle_address_operator */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #if GNU_EXTENSIONS_ALLOWED static void scan_address_of_label_expression(an_operand *result) /* Scan the GNU extended "&&label" expression, which evaluates to the address (as a void *) of the label. The "&&" operator is the current token on entry. */ { a_label_ptr label = NULL; a_constant_ptr constant = local_constant(); a_source_position start_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ a_boolean err = FALSE; db_enter(4, "scan_address_of_label_operator"); /* Save the current source position. */ copy_source_position(pos_curr_token, start_position); if (!gnu_mode) { /* Address-of-label only recognized in GNU modes. */ expr_pos_error(ec_nonstd_address_of_label, &start_position); err = TRUE; } else if (curr_expr_kind_is_traditional_const()) { if (curr_expr_kind_is(ek_pp)) { /* Address-of-label not allowed in preprocessing expressions. */ expr_pos_error(ec_bad_pp_operator, &start_position); err = TRUE; } else if (curr_expr_kind_is(ek_integral_constant)) { /* Address-of-label not allowed in integral constant expressions. */ expr_pos_error(ec_bad_integral_operator, &start_position); err = TRUE; } else if (curr_expr_kind_is(ek_template_arg)) { /* Address-of-label not allowed in a template argument expression. */ expr_pos_error(ec_bad_templ_arg_expr_operator, &start_position); err = TRUE; } /* if */ } else { report_gnu_extension_if_needed(&pos_curr_token, ec_nonstd_address_of_label); } /* if */ /* Scan the operand. This must be a single label. */ (void)get_token(); check_assertion(curr_token == tok_identifier); #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ if (depth_innermost_function_scope != NO_SCOPE_DEPTH) { label = scan_label(/*is_definition=*/FALSE, /*is_declaration=*/FALSE); } else { expr_pos_error(ec_nonlocal_label_reference, &pos_curr_token); /* Skip over the identifier without attempting to create a label. */ (void)get_token(); err = TRUE; } /* if */ if (err) { make_error_operand(result); } else { /* Create a constant operand representing the label. */ set_label_address_constant(label, constant); make_constant_operand(constant, result); } /* else */ result->state = (an_operand_state)os_prvalue; set_operand_position(result, &start_position, &end_position, &start_position); record_operator_position_in_rescan_info(result, &start_position, NO_TOKEN_SEQUENCE_NUMBER, (a_source_position *)NULL); rule_out_expr_kinds(ROEK_INTEGRAL_CONSTANT, result); release_local_constant(&constant); db_exit(); } /* scan_address_of_label_expression */ an_expr_node_ptr scan_asm_operand_expression(a_boolean output, a_boolean input, a_boolean is_memory_operand) /* Scan and return the expression associated with an asm operand. This is similar to scan_integer_expression with slightly different checks. output is TRUE for output operands; input is TRUE for input operands. If both input and output are TRUE, the operand is first read from and then written to. is_memory_operand is TRUE when the operand refers to memory (i.e., an address), in which case a conversion is not used. */ { an_expr_node_ptr expression; an_operand result; an_expr_stack_entry *saved_expr_stack; an_expr_stack_entry expr_stack_entry; a_boolean processed = FALSE; db_enter(3, "scan_asm_operand_expression"); save_expr_stack(&saved_expr_stack); push_expr_stack((an_expression_kind)ek_normal, &expr_stack_entry, /*force_object_lifetime=*/TRUE, /*suppress_object_lifetime=*/FALSE); /* Scan the expression. */ scan_expr(&result, PREC_LOWEST, EOPT_NO_OPTIONS); /* Convert from a class type to an integer if necessary. */ if (!C_mode() && !is_memory_operand && is_class_struct_union_type(result.type)) { try_to_convert_class_operand_to_builtin_type( &result, (a_type_ptr)NULL, BTK_INTEGRAL | BTK_ENUM | BTK_FLOATING | BTK_POINTER | BTK_BOOL, CCO_ALLOW_EXPLICIT_CONV_FUNCTIONS, &processed); } /* if */ if (!processed) { /* Non-class (i.e., normal) case. */ a_transformation_options_set options = TOPT_NO_OPTIONS; if (output) { options |= TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION | TOPT_SUPPRESS_ARRAY_TO_POINTER_CONVERSION | TOPT_SUPPRESS_FUNCTION_TO_POINTER_CONVERSION; } /* if */ do_operand_transformations(&result, options); } /* if */ if (output && !(is_template_dependent_context() && is_template_param_type(result.type))) { /* Logically, output operands must be modifiable lvalues. However, various versions of the GNU C and C++ compilers accept different kinds of lvalues (and the outcome is sometimes dependent on the platform). In particular, version 4.0, appears to accept const lvalues and some versions of the GNU C (but not GNU C++) compiler also accept void lvalues. Since some version-specific behavior appears to be platform dependent, we issue a warning for all values of gnu_version in cases that are known to be accepted on at least one version. (Lvalueness cannot reliably be determined for some template-dependent constructs. We therefore don't check the constraint in those cases until a real instantiation.) */ a_type_ptr type = result.type; an_error_severity sev = es_none; an_error_code diag = ec_expr_not_a_modifiable_lvalue; revert_gcc_rvalue_to_lvalue_if_possible_full(&result, /*ignore_casts=*/TRUE, /*drop_same_size_casts=*/TRUE); complete_type_is_needed(type); if (!is_an_lvalue(&result)) { sev = es_error; } else if (is_void_type(type)) { sev = gcc_mode ? es_warning : es_error; } else if (is_incomplete_type(type)) { sev = es_error; } else if (is_const_qualified_type(type) || (is_class_struct_union_type(type) && skip_typerefs(type) ->variant.class_struct_union.any_const_member)) { sev = es_warning; } /* if */ if (sev != es_none) { expr_pos_diagnostic(sev, diag, &result.position); } /* if */ if ((int)sev <= (int)es_warning) { modifying_lvalue(&result, /*value_used=*/input); } else { conv_to_error_operand(&result); } /* if */ } /* if */ expression = make_node_from_operand(&result); expression = wrap_up_full_expression(expression); pop_expr_stack(); restore_expr_stack(saved_expr_stack); #if EXTRA_SOURCE_POSITIONS_IN_IL curr_construct_end_position = result.end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ #if DEBUG if (debug_level >= 3) { db_expression(expression); } /* if */ #endif /* DEBUG */ db_exit(); return expression; } /* scan_asm_operand_expression */ #endif /* GNU_EXTENSIONS_ALLOWED */ static void scan_indirection_operator(a_rescan_control_block *rcblock, an_operand *result) /* Scan the "*" (indirection) operator. The current token is the operator. Scan the operand, build an expression, and return an operand for that in *result. If rcblock is non-NULL, redo semantic analysis on a previously-scanned expression, and return the result in *result (or an error indication in *rcblock). */ { an_operand operand; a_source_position operator_position; a_token_sequence_number operator_tok_seq_number; a_boolean err = FALSE, processed = FALSE; db_enter(4, "scan_indirection_operator"); if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. */ check_assertion(rcblock->operator_token == tok_star); make_rescan_operands(rcblock, &operand, (an_operand *)NULL, (an_operand *)NULL, &operator_position, &operator_tok_seq_number, (a_source_position *)NULL); } else { /* Normal, non-rescan, processing: Scan the operator and the operand. */ operator_position = pos_curr_token; operator_tok_seq_number = curr_token_sequence_number; (void)get_token(); scan_expr(&operand, PREC_PREFIX, EOPT_NO_OPTIONS); } /* if */ if (curr_expr_kind_is_traditional_const()) { if (curr_expr_kind_is(ek_pp)) { /* Address indirection not allowed in preprocessing expressions. */ expr_pos_error(ec_bad_pp_operator, &operator_position); err = TRUE; } else if (curr_expr_kind_is(ek_integral_constant)) { /* Address indirection not allowed in integral constant expressions. */ expr_pos_error(ec_bad_integral_operator, &operator_position); err = TRUE; } else if (curr_expr_kind_is(ek_template_arg)) { /* Address indirection not allowed in a template argument expression, except that Microsoft compilers do permit it sometimes on dependent constants (the indirection may be folded away at instantiation time). */ if (microsoft_mode && is_constant_operand(&operand)) { if (operand.variant.constant.kind == (a_constant_repr_kind)ck_template_param) { prep_generic_operand_full(&operand, /*lvalue_expected=*/FALSE, /*rvalue_expected=*/TRUE); } /* if */ } else { expr_pos_error(ec_bad_templ_arg_expr_operator, &operator_position); err = TRUE; } /* if */ } /* if */ } /* if */ if (err) { /* Operator is not allowed in this kind of expression. */ make_error_operand(result); operand_will_not_be_used_because_of_error(&operand); } else { a_type_ptr opnd_type = operand.type; if (is_any_reference_type(opnd_type)) { opnd_type = type_pointed_to(opnd_type); } /* if */ if (C_dialect == C_dialect_cplusplus && /* Note: not is_overloadable_type_first_operand on purpose; we want to handle pointer-to-template-param better than the generic way. Also, in C++/CLI mode *"string" shouldn't consider the possibility of converting the string literal to a handle to System::String. */ is_overloadable_first_operand_type(opnd_type)) { a_boolean has_predef_meaning = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED /* C++/CLI allows overloading on a handle, but it also allows the predefined meaning of "*" on that handle. */ if (cli_or_cx_enabled && is_handle_type(operand.type)) { has_predef_meaning = TRUE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Look for C++ operator overloading cases. */ check_for_operator_overloading((an_opname_kind)onk_star, /*unary_operator=*/TRUE, /*must_be_member_function=*/FALSE, /*try_conversions=*/!has_predef_meaning, has_predef_meaning, &operand, (an_operand *)NULL, &operator_position, operator_tok_seq_number, (a_nondependent_call_depth)0, (a_source_position *)NULL, result, &processed); } /* if */ if (!processed) { /* Non-operator-function cases. */ do_operand_transformations(&operand, TOPT_NO_OPTIONS); if ( #if MICROSOFT_EXTENSIONS_ALLOWED (cli_or_cx_enabled && is_handle_type(operand.type)) || #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ is_template_dependent_type(operand.type) || check_pointer_operand(&operand, ec_bad_indirection_operand)) { a_type_ptr operand_type; an_expr_node_ptr node; /* Make the "*" operator node. */ if (is_pointer_or_handle_type(operand.type)) { operand_type = type_pointed_to(operand.type); } else if (is_template_dependent_type(operand.type)) { operand_type = type_of_unknown_templ_param_nontype; } else { expect_error(); operand_type = error_type(); } /* if */ node = make_lvalue_operator_node((an_expr_operator_kind)eok_indirect, operand_type, make_node_from_operand(&operand)); #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled) { node = unbox_after_indirection_if_required(node); } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ make_glvalue_expression_operand(node, result); if (is_void_type(operand_type)) { /* Indirection through a void * pointer. */ if (!C_mode()) { /* In C++ mode, this is an error.*/ error_in_operand(ec_expr_not_object_pointer, &operand); conv_to_error_operand(result); } else { /* In C mode, indirection through a void * pointer is valid, but the result is not an lvalue. For example, void *p; *p; is legal, but *p is not an lvalue. This was discussed in Defect Report 12 of the C standards committee, with further discussion in Defect Report 106. While those interpretations make it clear that this applies for void *, they seem to leave out cv-qualified void *; those apparently still convert to an lvalue. (In GNU modes, the result is always an lvalue). */ if (!is_qualified_type(operand_type) && !gcc_mode) { result->state = (an_operand_state)os_prvalue; node->is_lvalue = FALSE; } /* if */ } /* if */ } /* if */ if (is_an_lvalue(result)) { /* Transfer references because the result is an lvalue. */ result->ref_entries_list = operand.ref_entries_list; } /* if */ } else { /* There was some error in the operand. */ make_error_operand(result); } /* if */ } /* if */ } /* if */ set_operand_position(result, &operator_position, &operand.end_position, &operator_position); record_operator_position_in_rescan_info(result, &operator_position, operator_tok_seq_number, (a_source_position *)NULL); rule_out_expr_kinds(ROEK_INTEGRAL_CONSTANT, result); db_exit(); } /* scan_indirection_operator */ static void diagnose_bad_template_arg_operation(a_source_position *err_pos) /* Issue a diagnostic about an operation at the indicated source position that is invalid within a template argument expression. */ { expr_pos_error(ec_non_integral_operation_in_templ_arg, err_pos); } /* diagnose_bad_template_arg_operation */ static void check_for_bad_template_arg_operation( an_operand *operand_1, an_operand *operand_2, a_source_position *operator_position, an_operand *result, a_boolean *processed) /* Check for a non-integral operation in a template argument expression. operand_1 and operand_2 are the operands of the operation. *operator_position gives the source position of the operator. If an error is detected, a diagnostic is issued, *result is set to an error operand, and *processed is set to TRUE. */ { if (curr_expr_kind_is_traditional_const() && (is_bad_type_for_template_arg_operand(operand_1->type) || (operand_2 != NULL && is_bad_type_for_template_arg_operand(operand_2->type)))) { diagnose_bad_template_arg_operation(operator_position); make_error_operand(result); operand_will_not_be_used_because_of_error(operand_1); if (operand_2 != NULL) { operand_will_not_be_used_because_of_error(operand_2); } /* if */ *processed = TRUE; } /* if */ } /* check_for_bad_template_arg_operation */ static void scan_arith_prefix_operator(a_rescan_control_block *rcblock, an_operand *result) /* Scan the "+", "-", "~", and "!" prefix operators. The current token is the operator. Scan the operand, build an expression, and return an operand for that in *result. If rcblock is non-NULL, redo semantic analysis on a previously-scanned expression, and return the result in *result (or an error indication in *rcblock). */ { a_token_kind operator_token; an_operand operand; an_expr_operator_kind op = (an_expr_operator_kind)eok_error; a_source_position operator_position; a_token_sequence_number operator_tok_seq_number; a_type_ptr result_type = NULL; a_boolean do_promotion, processed = FALSE; db_enter(4, "scan_arith_prefix_operator"); if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. */ operator_token = rcblock->operator_token; make_rescan_operands(rcblock, &operand, (an_operand *)NULL, (an_operand *)NULL, &operator_position, &operator_tok_seq_number, (a_source_position *)NULL); } else { /* Normal, non-rescan, processing. */ a_local_expr_options_set eopts = EOPT_NO_OPTIONS; operator_token = curr_token; if (operator_token == tok_not && gcc_mode) { eopts |= EOPT_LOGICAL_NOT_OPERAND; } /* if */ operator_position = pos_curr_token; operator_tok_seq_number = curr_token_sequence_number; /* Scan the operand. */ (void)get_token(); scan_expr(&operand, PREC_PREFIX, eopts); } /* if */ if (C_dialect == C_dialect_cplusplus && is_overloadable_type_first_operand(&operand)) { /* Look for C++ operator overloading cases. */ a_boolean has_predef_meaning = FALSE; #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && operator_token == tok_not && is_handle_type(operand.type)) { has_predef_meaning = TRUE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ check_for_operator_overloading(opname_kind_for_token[(int)operator_token], /*unary_operator=*/TRUE, /*must_be_member_function=*/FALSE, /*try_conversions=*/TRUE, has_predef_meaning, &operand, (an_operand *)NULL, &operator_position, operator_tok_seq_number, (a_nondependent_call_depth)0, (a_source_position *)NULL, result, &processed); } /* if */ if (!processed && curr_expr_kind_is(ek_template_arg) && curr_expr_kind_is_traditional_const()) { if (is_bad_type_for_template_arg_operand(operand.type) && /* Allow negation of a floating point constant. */ !((floating_point_template_parameters_allowed || microsoft_mode) && operator_token == tok_minus && is_floating_type(operand.type) && is_constant_operand(&operand))) { /* Non-integral operations are not allowed in a template argument. */ diagnose_bad_template_arg_operation(&operator_position); make_error_operand(result); operand_will_not_be_used_because_of_error(&operand); processed = TRUE; } /* if */ } /* if */ if (!processed) { /* Non-operator-function cases. */ do_operand_transformations(&operand, TOPT_NO_OPTIONS); /* Check the type of the operand. */ do_promotion = TRUE; switch (operator_token) { case tok_plus: op = (an_expr_operator_kind)eok_unary_plus; if (C_dialect == C_dialect_cplusplus && is_pointer_type(operand.type)) { /* In C++, the operand may be a pointer (ARM 5.3). */ } else { /* In C++ or C, the operand may be arithmetic or enum. */ #if GNU_VECTOR_TYPES_ALLOWED if (gnu_mode && is_vector_type(operand.type)) { /* Vector types are arithmetic types in some sense. */ } else #endif /* GNU_VECTOR_TYPES_ALLOWED */ /* Do not insert code here. */ #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && is_scoped_enum_type(operand.type)) { /* In C++/CLI mode, a scoped enumeration operand is permitted. No promotion is involved. */ do_promotion = FALSE; result_type = operand.type; } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here. */ { (void)check_arithmetic_or_enum_operand(&operand); } /* if */ } /* if */ break; case tok_not: op = (an_expr_operator_kind)eok_not; do_promotion = FALSE; #if GNU_VECTOR_TYPES_ALLOWED if (gnu_mode && !C_mode() && is_vector_type(operand.type)) { /* Vector types are arithmetic types in some sense. The result is a vector of integers with the same number of elements as the operand. */ op = (an_expr_operator_kind)eok_vector_not; /* The result of this vector operation is a vector of integers with the same number of elements as the operands. */ a_type_ptr vec_type = skip_typerefs(operand.type), elem_type; elem_type = skip_typerefs(vec_type->variant.vector.element_type); if (is_bool_type(elem_type)) { /* A vector of bool type (a Clang-only feature -- the GCC vector_size attribute doesn't permit bool elements). The result of a "not" operation on such a "vector of bool" is also a vector of bool type. */ result_type = vec_type; } else { /* "Not" applied to vector types in GCC results in a vector of signed integers. */ result_type = make_integer_vector_result_type(operand.type); } /* if */ } else #endif /* GNU_VECTOR_TYPES_ALLOWED */ /* Do not insert code here. */ { (void)check_boolean_controlling_expr(&operand); result_type = boolean_result_type(); } /* if */ break; case tok_minus: op = (an_expr_operator_kind)eok_negate; /* Check that the type is arithmetic, with some caveats. */ #if FIXED_POINT_ALLOWED if (is_fixed_point_type(operand.type)) { if (f_skip_typerefs(operand.type)->variant.fixed_point.is_unsigned) { /* Warn on negation of an unsigned value. */ expr_pos_warning(ec_unsigned_fixed_point_negation, &operator_position); } /* if */ } else #endif /* FIXED_POINT_ALLOWED */ /* Do not insert code here. */ #if GNU_VECTOR_TYPES_ALLOWED if (gnu_mode && is_vector_type(operand.type)) { /* Vector types are arithmetic types in some sense. */ } else #endif /* GNU_VECTOR_TYPES_ALLOWED */ /* Do not insert code here. */ #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && is_scoped_enum_type(operand.type)) { /* In C++/CLI mode, a scoped enumeration operand is permitted. No promotion is involved. */ do_promotion = FALSE; result_type = operand.type; } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here. */ { (void)check_arithmetic_or_enum_operand(&operand); } /* if */ break; case tok_compl: #if C99_IL_EXTENSIONS_SUPPORTED if (gnu_mode && is_complex_type(operand.type)) { op = (an_expr_operator_kind)eok_xconj; } else #endif /* C99_IL_EXTENSIONS_SUPPORTED */ /* Do not insert code here. */ { op = (an_expr_operator_kind)eok_complement; #if GNU_VECTOR_TYPES_ALLOWED if (gnu_mode && is_vector_type(operand.type)) { /* Vector types are arithmetic types in some sense. */ a_type_ptr elem_type = skip_typerefs(operand.type)->variant.vector.element_type; if (!is_integral_or_enum_type(elem_type) && !is_template_param_type(elem_type)) { error_and_make_error_operand( ec_vector_operation_requires_integer_vector, &operand); } /* if */ } else #endif /* GNU_VECTOR_TYPES_ALLOWED */ /* Do not insert code here. */ #if MICROSOFT_EXTENSIONS_ALLOWED if (cli_or_cx_enabled && is_scoped_enum_type(operand.type)) { /* In C++/CLI mode, a scoped enumeration operand is permitted. No promotion is involved. */ do_promotion = FALSE; result_type = operand.type; } else #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ /* Do not insert code here. */ { (void)check_integral_or_enum_operand(&operand); } /* if */ } /* if */ break; default: unexpected_condition_str("scan_arith_prefix_operator: bad operator"); } /* switch */ if (do_promotion) { /* Do integral promotions if required. */ promote_operand(&operand); result_type = operand.type; } /* if */ /* Build the IL for the operation. */ do_unary_operation(op, &operand, result_type, result, &operator_position, operator_tok_seq_number); if (strict_ansi_mode && (C_mode() || !constexpr_enabled) && is_floating_type(result_type) && is_constant_operand(result)) { /* Floating-point literals can appear in C-style (and pre-C++11-style) constant-expressions, provided they are the immediate operand of a cast. That does not allow for a "+" or "-" prefix. */ rule_out_expr_kinds(ROEK_INTEGRAL_CONSTANT, result); } else if (microsoft_mode && is_constant_operand(result) && is_constant_operand(&operand) && !operand.variant.constant.null_pointer_constant_ruled_out) { /* Microsoft accepts -0, +0, and ~~0 as null pointer constants, even in non-permissive modes. */ if (operator_token == tok_plus || operator_token == tok_minus || operator_token == tok_compl) { result->variant.constant.null_pointer_constant_ruled_out = FALSE; } /* if */ } /* if */ } /* if */ set_operand_position(result, &operator_position, &operand.end_position, &operator_position); db_exit(); } /* scan_arith_prefix_operator */ static an_expr_node_ptr make_sizeof_expr(an_expr_node_kind kind, a_boolean is_type, a_type_ptr type, an_operand *operand, an_operand *result) /* Create a node for a sizeof-like construct of the given kind (enk_sizeof, enk_alignof, or enk_datasizeof), and return a pointer to it. If is_type is TRUE, the operator applies to a type and "type" indicates that type (e.g., "sizeof(int*)"). If is_type is FALSE, the operator applies to the expression described by "operand". If result is non-NULL, an operand for the result is built and returned there. */ { an_expr_node_ptr node = alloc_expr_node(kind); node->type = integer_type(targ_size_t_int_kind); node->variant.sizeof_info.is_type = is_type; if (is_type) { /* sizeof(type). */ node->variant.sizeof_info.variant.type = type; } else { /* sizeof expression. */ an_expr_node_ptr expr; if (is_template_dependent_context() && is_template_dependent_type(operand->type)) { /* An expression in a prototype instantiation. */ prep_generic_operand(operand); } /* if */ eliminate_unusual_operand_kinds(operand); expr = make_node_from_operand(operand); node->variant.sizeof_info.variant.expr = expr; /* Make sure the referenced flag is set on a VLA variable. */ expr = skip_parens(expr); if (expr->is_lvalue && is_variable_node(expr)) { a_variable_ptr var = node_variable(expr); var->source_corresp.referenced = TRUE; } /* if */ } /* if */ if (result != NULL) { /* Build an operand for the result. */ make_expression_operand(node, result); } /* if */ return node; } /* make_sizeof_expr */ static void switch_to_scope_region_and_lifetime( a_scope_depth scope_depth, a_memory_region_number *region_to_switch_back_to, an_object_lifetime_ptr *saved_object_lifetime) /* Switch to the memory region associated with the given scope depth if not already there. Also change the current object lifetime if appropriate. Set region_to_switch_back_to and saved_object_lifetime for use later by switch_back_region_and_lifetime. */ { switch_to_scope_region(scope_depth, region_to_switch_back_to); *saved_object_lifetime = curr_object_lifetime; if (curr_object_lifetime != NULL && scope_stack[scope_depth].kind == sck_function && scope_stack[scope_depth].curr_scope_object_lifetime != NULL) { curr_object_lifetime = scope_stack[scope_depth].curr_scope_object_lifetime; } /* if */ } /* switch_to_scope_region_and_lifetime */ static void switch_back_region_and_lifetime( a_memory_region_number region_to_switch_back_to, an_object_lifetime_ptr saved_object_lifetime) /* Switch back to the memory region and object lifetime that were current when switch_to_file_scope_region_and_lifetime was called. */ { switch_back_to_original_region(region_to_switch_back_to); curr_object_lifetime = saved_object_lifetime; } /* switch_back_region_and_lifetime */ static void scan_sizeof_pack_operator(a_rescan_control_block *rcblock, an_operand *result) /* Scan the variadic template sizeof... operator, which gives the number of elements in a parameter pack. For example: sizeof...(T) The current token is the sizeof keyword. Scan the operand, and return an operand for sizeof... applied to that, in *operand. If rcblock is non-NULL, redo semantic analysis on a previously-scanned sizeof... expression, and return the result in *result (or an error indication in *rcblock). */ { a_source_position start_position, id_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position, id_end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ a_symbol_ptr sym = NULL; a_boolean err = FALSE; a_boolean any_more; an_expr_stack_entry expr_stack_entry; a_host_large_unsigned result_count = 0, additional_non_pack_count = 0; a_pack_expansion_stack_entry_ptr pesep = NULL; a_pack_expansion_descr_ptr pedep = NULL; a_memory_region_number region_to_switch_back_to = NULL_region_number; a_symbol_locator field_locator; a_boolean locator_set = FALSE; db_enter(4, "scan_sizeof_pack_operator"); id_position = null_source_position; push_expr_stack_with_rcblock((an_expression_kind)ek_sizeof, &expr_stack_entry, /*force_object_lifetime=*/FALSE, /*suppress_object_lifetime=*/FALSE, rcblock); expr_stack->unevaluated_expr_will_be_kept_in_il = TRUE; if (rcblock == NULL) { /* Scanning from source. */ if (!is_variadic_template_context()) { expr_pos_error(ec_sizeof_pack_in_non_variadic_context, &pos_curr_token); err = TRUE; } /* if */ start_position = pos_curr_token; check_assertion(curr_token == tok_sizeof); (void)get_token(); check_assertion(curr_token == tok_ellipsis); any_more = begin_potential_pack_expansion_context(&pesep); /* In an expansion of a pack, we're looping here only to count the number of times around the loop. We step over the tokens but we don't really need to do anything with them. */ while (any_more) { result_count++; if (is_variadic_template_context()) { record_pack_expansion_ellipsis(); } else { (void)get_token(); } /* if */ (void)required_token(tok_lparen, ec_exp_lparen); add_matching_stop_token(tok_rparen); if (is_resolved_id_pseudo_token(curr_token)) { a_symbol_ptr ifc_ref_sym = resolve_id_pseudo_token_to_sym(); if (ifc_ref_sym == NULL) { err = TRUE; } else { make_resolved_id_pseudo_token_locator(ifc_ref_sym, &locator_for_curr_id); locator_for_curr_id.source_position = pos_curr_token; curr_token = tok_identifier; } /* if */ } /* if */ if (curr_token != tok_identifier) { expr_syntax_error(ec_exp_identifier); err = TRUE; } else { id_position = pos_curr_token; #if EXTRA_SOURCE_POSITIONS_IN_IL id_end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* Look up the identifier. It must be a parameter pack name. */ sym = normal_id_lookup(&locator_for_curr_id, IDL_NO_OPTIONS); if (sym == NULL) { expr_pos_error(ec_sizeof_operand_not_parameter_pack, &pos_curr_token); err = TRUE; } else { if (sym->kind != (a_symbol_kind)sk_overloaded_function) { /* Record a reference against the identifier. */ (void)ref_entry(sym, &pos_curr_token); } /* if */ if (symbol_is(sym, sk_field) && !locator_set) { /* For a field (representing an init-capture), save the locator for use later. */ field_locator = locator_for_curr_id; locator_set = TRUE; } /* if */ /* When the instantiation_descr is non-NULL this is a nondependent expansion occurring in a prototype instantiation context (for example a template declaration inside a real instantiation of a class). */ if (is_template_dependent_context() && pesep != NULL && pesep->instantiation_descr == NULL) { /* Check that the identifier is a parameter pack name. */ if (!symbol_is_pack(sym)) { expr_pos_error(ec_sizeof_operand_not_parameter_pack, &pos_curr_token); suppress_expansion_with_no_packs_diagnostic(pesep); err = TRUE; } else { record_potential_pack_reference(sym, &pos_curr_token); } /* if */ } /* if */ } /* if */ /* Advance past the identifier. */ (void)get_token(); } /* if */ (void)required_token_no_advance(tok_rparen, ec_exp_rparen); remove_matching_stop_token(tok_rparen); pedep = end_potential_pack_expansion_context(pesep, /*is_declarator=*/TRUE); if (pedep == NULL) { additional_non_pack_count++; } /* if */ any_more = advance_to_next_pack_element(pesep); } /* while */ #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ /* Skip over the closing right parenthesis. */ if (curr_token == tok_rparen) (void)get_token(); } else { /* Rescanning a previously-scanned sizeof... */ an_expr_rescan_info_entry_ptr eriep; a_boolean rescan_err; Value_saver unexpanded_pack_saver( &rcblock->ctws_state->unexpanded_pack); check_assertion(rcblock->expr->kind == (an_expr_node_kind)enk_sizeof_pack); eriep = get_expr_rescan_info(rcblock->expr, (an_expr_rescan_info_entry *)NULL); start_position = eriep->saved_operand.position; #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = eriep->saved_operand.end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ pedep = eriep->saved_operand.pack_expansion_descr; if (pedep == NULL) { /* In some error cases, pedep can be NULL. */ rescan_err = TRUE; any_more = FALSE; } else if ((rcblock->options & (CTWS_PRESERVE_DEDUCED_PACKS | CTWS_ADJUST_COORDINATES)) != 0 || ((rcblock->options & CTWS_ALIAS_DEDUCTION_GUIDE) != 0 && pack_expansion_maps_to_unexpanded_pack( pedep, rcblock->template_param_list, rcblock->template_arg_list))) { /* Just copy the saved operand for another rescan later on. */ copy_operand(&rcblock->expr->extra.rescan_info->saved_operand, result); make_template_param_expr_constant_operand(result); goto operand_ready; } else { rcblock->ctws_state->unexpanded_pack = FALSE; any_more = begin_rescan_pack_expansion_context( pedep, rcblock->template_param_list, rcblock->template_arg_list, &pesep, rcblock->options, rcblock->ctws_state, &rescan_err); } /* if */ /* Check if an error occurred (such as mismatched parameter pack lengths). */ if (rescan_err) subst_fail(rcblock->error_detected); if (!any_more && (rcblock->options & CTWS_MAY_BE_RESCANNED) != 0 && rcblock->ctws_state->unexpanded_pack) { /* For an unexpanded pack, just copy the saved operand for another rescan later on. */ copy_operand(&rcblock->expr->extra.rescan_info->saved_operand, result); make_template_param_expr_constant_operand(result); goto operand_ready; } /* if */ /* Again, we loop only to count the number of times around. */ while (any_more) { result_count++; (void)end_potential_pack_expansion_context(pesep, /*is_declarator=*/TRUE); any_more = advance_to_next_pack_element(pesep); } /* while */ } /* if */ if (err) { /* Some previous error. */ make_error_operand(result); } else if (rcblock == NULL && ((is_alias_in_template_decl_context() && enclosing_scope_is_prototype_instantiation_context()) || (is_template_dependent_context() && (pedep != NULL || in_ms_nonreal_class_instantiation())))) { /* For the prototype instantiation, return an enk_sizeof_pack expression as a template constant. When pedep is NULL, this is a nondependent expansion in a prototype instantiation context. If an alias template is instantiated in a template declaration scope, we need to preserve a dependent sizeof... if the enclosing context is a prototype instantiation. */ an_expr_node_ptr expr; if (symbol_is(sym, sk_variable)) { a_variable_ptr vp = sym->variant.variable.ptr; a_routine_ptr rp = vp->source_corresp.enclosing_routine; if (rp != NULL) { a_scope_depth depth = scope_for_routine(rp)->depth_in_scope_stack; check_assertion(depth != NO_SCOPE_DEPTH); switch_to_scope_region(depth, ®ion_to_switch_back_to); } /* if */ } /* if */ expr = alloc_expr_node((an_expr_node_kind)enk_sizeof_pack); expr->type = integer_type(targ_size_t_int_kind); if (is_type_symbol(sym)) { expr->variant.sizeof_pack.is_type = TRUE; expr->variant.sizeof_pack.variant.type = sym->variant.type.ptr; } else if (sym->kind == (a_symbol_kind)sk_class_template) { a_template_ptr templ = sym->variant.template_info->il_template_entry; check_assertion(templ != NULL && templ->is_pack); expr->variant.sizeof_pack.is_type = FALSE; expr->variant.sizeof_pack.is_template_template = TRUE; expr->variant.sizeof_pack.variant.templ = templ; } else { an_operand operand; expr->variant.sizeof_pack.is_type = FALSE; /* Make an expression node for the parameter pack. */ if (sym->kind == (a_symbol_kind)sk_variable) { make_lvalue_variable_operand(sym->variant.variable.ptr, &id_position, end_position_or_null(&id_end_position), &operand, (a_ref_entry *)NULL); } else if (sym->kind == (a_symbol_kind)sk_parameter) { make_param_ref_operand(&operand, sym); } else if (sym->kind == (a_symbol_kind)sk_constant) { make_sym_constant_operand(sym, &operand); } else if (symbol_is(sym, sk_field) && locator_set) { make_field_operand(&field_locator, &id_position, end_position_or_null(&id_end_position), &operand); } else { unexpected_condition(); } /* if */ set_operand_position(&operand, &id_position, &id_end_position, (a_source_position *)NULL); expr->variant.sizeof_pack.variant.expr = make_node_from_operand(&operand); } /* if */ make_expression_operand(expr, result); if (pedep != NULL) mark_operand_as_pack_expansion(result, pedep); /* Wrap the enk_sizeof_pack expression in a template parameter constant so we produce a constant result. */ make_template_param_expr_constant_operand(result); /* Clear the pack expansion indication on the resulting operand because we've already handled the pack expansion at the sizeof... level. We don't want the is_pack_expansion flag set on the enk_sizeof_pack expression. */ result->pack_expansion_descr = NULL; } else { /* For a real instantiation or a rescan, return the constant size of the parameter pack. */ a_constant_ptr constant = local_constant(); set_unsigned_integer_constant(constant, result_count, targ_size_t_int_kind); make_constant_operand(constant, result); release_local_constant(&constant); additional_non_pack_count = 0; } /* if */ operand_ready: set_operand_position(result, &start_position, &end_position, &start_position); record_operator_position_in_rescan_info(result, &start_position, NO_TOKEN_SEQUENCE_NUMBER, (a_source_position *)NULL); if (additional_non_pack_count != 0) { /* If we have preserved a dependent sizeof..., but also have some non-pack elements, we need to add those to the resulting expression. */ an_operand op1 = *result, op2; a_constant_ptr constant = local_constant(); set_unsigned_integer_constant(constant, additional_non_pack_count, targ_size_t_int_kind); make_constant_operand(constant, &op2); release_local_constant(&constant); do_binary_operation(eok_add, &op1, &op2, result->type, result, &start_position, NO_TOKEN_SEQUENCE_NUMBER); } /* if */ if (region_to_switch_back_to != NULL_region_number) { switch_back_to_original_region(region_to_switch_back_to); } /* if */ pop_expr_stack(); db_exit(); } /* scan_sizeof_pack_operator */ #if GNU_EXTENSIONS_ALLOWED static a_boolean curr_expr_has_gnu_statement_expression(void) /* Return whether the current expression contains a GNU statement expression. */ { a_boolean result = FALSE; a_scope_ptr scope = expr_stack->last_subscope_preceding_expr; if (scope == NULL) { scope = scope_stack_top().first_scope; } /* if */ for (; scope != NULL; scope = scope->next) { if (scope->is_stmt_expr_block) { result = TRUE; break; } /* if */ } /* for */ return result; } /* curr_expr_has_gnu_statement_expression */ #else /* !GNU_EXTENSIONS_ALLOWED */ /*lint -emacro(506,curr_expr_has_gnu_statement_expression)*/ #define curr_expr_has_gnu_statement_expression() FALSE #endif /* GNU_EXTENSIONS_ALLOWED */ static a_scope_depth scope_depth_to_allocate_unevaluated_operand(void) /* We are about to scan the argument expression for an unevaluated operand. Compute a scope depth from which a memory region for the expression can be determined. Ordinarily, depth_scope_stack will do, but if we are inside a local class, we want the expression tree to be stored in the function memory region. For example: void f() { int x; struct { decltype(x) m; } y; } // The struct type is stored in file scope memory, but the decltype // argument must be able to refer to "x", and must therefore be // stored in f's memory region. */ { a_scope_depth result = depth_scope_stack; if (depth_innermost_function_scope != NO_SCOPE_DEPTH) { result = depth_innermost_function_scope; } else if (inside_local_class) { a_scope_ptr sp = get_innermost_function_scope(); if (sp != NULL) { result = sp->depth_in_scope_stack; } else { /* This can happen if an instantiation scope for a lambda has been pushed but the function scope for the call operator has not yet. */ result = DEPTH_OF_FILE_SCOPE; } /* if */ } /* if */ return result; } /* scope_depth_to_allocate_unevaluated_operand */ static void discard_statement_expr_refs_in_operand(an_operand *opnd) /* opnd is being discarded without preserving its expression tree. If it contains GNU statement expressions, eliminate any IL entries that refer to statements in the dropped expression. */ { if (expr_stack->statement_expression_seen) { /* Statement expressions may have associated pragma and source sequence entries that need discarding as well. */ if (is_expression_operand(opnd)) { eliminate_statement_expr_pragmas(opnd->variant.expression); #if GENERATE_SOURCE_SEQUENCE_LISTS eliminate_statement_expr_src_seq_entries(opnd->variant.expression); #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ } else if (is_constant_operand(opnd) && opnd->variant.constant.expr != NULL) { eliminate_statement_expr_pragmas(opnd->variant.constant.expr); #if GENERATE_SOURCE_SEQUENCE_LISTS eliminate_statement_expr_src_seq_entries(opnd->variant.constant.expr); #endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ } /* if */ } /* if */ } /* discard_statement_expr_refs_in_operand */ static void unevaluated_operand_dropped(an_operand *opnd) /* The given operand is the unevaluated operand of something like a sizeof- expression, and its representation is not going to be kept around. Perform any adjustments needed due to discarding the expression. */ { discard_statement_expr_refs_in_operand(opnd); undo_side_effects_for_discarded_unevaluated_expression(); } /* unevaluated_operand_dropped */ static void scan_sizeof_operator(a_rescan_control_block *rcblock, an_operand *result) /* Scan the sizeof or (in some Clang modes) the __datasizeof operator. Syntax: sizeof ( type-id ) sizeof expression __datasizeof ( type-id ) __datasizeof expression Also, when variadic templates are enabled: sizeof... (pack-name) The current token is the sizeof or __datasizeof keyword. Scan a type or expression operand, and return an operand for sizeof applied to that, in *operand. If rcblock is non-NULL, redo semantic analysis on a previously- scanned sizeof or __datasizeof expression, and return the result in *result (or an error indication in *rcblock). */ { a_source_position operator_position, start_position, type_position; a_source_position lparen_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ an_operand operand; a_constant_ptr constant = local_constant(); a_boolean is_parenthesized = FALSE, is_type = FALSE; a_type_ptr sizeof_type = NULL, orig_sizeof_type = NULL; an_expr_stack_entry expr_stack_entry; a_boolean template_case = FALSE; #if UPC_EXTENSIONS_ALLOWED || CHECKING a_token_kind operator_token; #endif /* UPC_EXTENSIONS_ALLOWED || CHECKING */ #if UPC_EXTENSIONS_ALLOWED a_upc_block_size block_size; a_boolean multiply_by_threads_needed = FALSE; a_boolean use_special_upc_size = FALSE; a_upc_block_size special_upc_size = UPC_BLOCK_SIZE_INDEFINITE; a_boolean err = FALSE; #endif /* UPC_EXTENSIONS_ALLOWED */ a_boolean operand_was_created = FALSE, operand_was_used = FALSE; a_memory_region_number region_to_switch_back_to; an_object_lifetime_ptr saved_object_lifetime; a_boolean nonconstant_case = FALSE; a_boolean sizeof_itself_is_in_const_expr = curr_expr_kind_is_traditional_const(); a_boolean sizeof_itself_is_potentially_evaluated = curr_expr_is_potentially_evaluated(); an_expr_node_kind kind; db_enter(4, "scan_sizeof_operator"); if (variadic_templates_enabled) { /* Check for the variadic template sizeof...(T) case. */ a_boolean is_sizeof_pack = FALSE; if (rcblock != NULL) { if (rcblock->expr->kind == enk_sizeof_pack) { is_sizeof_pack = TRUE; } /* if */ } else { if (curr_token == tok_sizeof && next_token() == tok_ellipsis) { is_sizeof_pack = TRUE; } /* if */ } /* if */ if (is_sizeof_pack) { /* Handle sizeof...(T) in a separate routine. */ scan_sizeof_pack_operator(rcblock, result); goto end_of_routine; } /* if */ } /* if */ if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. */ a_token_sequence_number operator_tok_seq_number; #if UPC_EXTENSIONS_ALLOWED || CHECKING operator_token = rcblock->operator_token; #endif /* UPC_EXTENSIONS_ALLOWED || CHECKING */ kind = (an_expr_node_kind)rcblock->expr->kind; if (kind == enk_constant) { /* Presumably a tpck_sizeof or tpck_datasizeof constant. */ a_constant *cp = node_constant(rcblock->expr); check_assertion(constant_is(cp, ck_template_param)); if (tpck_is(cp, tpck_sizeof)) { kind = enk_sizeof; } else if (tpck_is(cp, tpck_datasizeof)) { kind = enk_datasizeof; } else { unexpected_condition(); } /* if */ } /* if */ make_sizeof_et_al_rescan_operands(rcblock, &is_type, &operand, &orig_sizeof_type, &operator_position, &operator_tok_seq_number, &type_position); sizeof_type = orig_sizeof_type; operand_was_created = !is_type; #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = rcblock->expr->expr_range.end; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ if (operand_was_created && microsoft_bugs && operand.is_address_of_id_expression && is_constant_operand(&operand) && constant_is(&operand.variant.constant, ck_ptr_to_member)) { record_suppressed_error(); } /* if */ } else { /* Normal, non-rescan, processing. */ #if UPC_EXTENSIONS_ALLOWED || CHECKING operator_token = curr_token; #endif /* UPC_EXTENSIONS_ALLOWED || CHECKING */ if (curr_token == tok_datasizeof) { kind = enk_datasizeof; } else { kind = enk_sizeof; } /* if */ operator_position = pos_curr_token; } /* if */ start_position = operator_position; #if UPC_EXTENSIONS_ALLOWED check_assertion(operator_token == tok_sizeof || operator_token == tok_datasizeof || operator_token == tok_upc_localsizeof || operator_token == tok_upc_elemsizeof || operator_token == tok_upc_blocksizeof); #else /* !UPC_EXTENSIONS_ALLOWED */ check_assertion(operator_token == tok_sizeof || operator_token == tok_datasizeof); #endif /* UPC_EXTENSIONS_ALLOWED */ #if CHECKING if (curr_expr_kind_is(ek_pp)) { /* Sizeof not possible for preprocessing expressions. */ internal_error("scan_sizeof_operator: in preprocessing expr"); } /* if */ #endif /* CHECKING */ /* If we're in the file-scope memory region instead of a function-scope memory region because we're scanning something like an array bound, switch back. Any expression nodes allocated must be in the function-scope memory region. */ switch_to_scope_region_and_lifetime( scope_depth_to_allocate_unevaluated_operand(), ®ion_to_switch_back_to, &saved_object_lifetime); push_expr_stack_with_rcblock((an_expression_kind)ek_sizeof, &expr_stack_entry, /*force_object_lifetime=*/FALSE, /*suppress_object_lifetime=*/FALSE, rcblock); expr_stack->unevaluated_expr_will_be_kept_in_il = TRUE; if (rcblock == NULL) { (void)get_token(); if (curr_token == tok_lparen) { /* A left parenthesis could indicate a type in parentheses or an expression in parentheses, i.e., sizeof (int) vs. sizeof (i) We can distinguish the two using the first token inside the parentheses. However, if the construct is an expression in parentheses, we must then scan it with a special flag indicating that a left parenthesis was trapped. It's not enough to just scan the expression to the matching right parenthesis, as shown by the following: sizeof (v).b The sizeof should be applied to "(v).b", not just "(v)". */ is_parenthesized = TRUE; copy_source_position(pos_curr_token, lparen_position); (void)get_token(); if (is_decl_not_expr(DFS_IS_SIZEOF | DFS_ABSTRACT_DECLARATOR_ALLOWED | DFS_SINGLE_TYPE_REQUIRED)) { /* This is a type-name in parentheses. */ is_type = TRUE; } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED } else if (ms_extensions && !C_mode()) { /* Microsoft allows "sizeof T" without parentheses in C++ mode, where T is a type-name (not a keyword like "int"). If we have a type name that is not followed by a left parenthesis, assume this to be the size of the type. Note that is_sizeof_context is not passed here. It is passed indirectly by is_decl_not_expr above. */ if (is_generalized_identifier_start(GID_IS_EXPR_CONTEXT) && next_token() != tok_lparen && curr_type_symbol(/*is_new_type_name=*/FALSE, /*in_prescan=*/FALSE, /*in_type_check=*/FALSE, /*is_implicit_type_context=*/FALSE, /*is_sizeof_context=*/TRUE)) { /* Something like typedef int I; sizeof I; but not sizeof I(); */ is_type = TRUE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ if (is_type) { type_position = pos_curr_token; if (is_parenthesized) { /* Scan the type-name for a parenthesized type. */ add_matching_stop_token(tok_rparen); orig_sizeof_type = scan_type_for_sizeof( sizeof_itself_is_potentially_evaluated); #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ (void)required_token(tok_rparen, ec_exp_rparen); remove_matching_stop_token(tok_rparen); if (compound_literals_allowed && curr_token == tok_lbrace) { /* Something like sizeof(int){37} -- the type is the beginning of a compound literal. */ scan_compound_literal(&orig_sizeof_type, &lparen_position, &type_position, (a_rescan_control_block *)NULL, (an_init_component *)NULL, result, EOPT_NO_OPTIONS); orig_sizeof_type = result->type; } /* if */ } else { /* Unparenthesized type, e.g., "sizeof T" (Microsoft extension). */ #if MICROSOFT_EXTENSIONS_ALLOWED orig_sizeof_type = simple_type_specifier_sequence(); #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = curr_construct_end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ #else /* !MICROSOFT_EXTENSIONS_ALLOWED */ unexpected_condition(); #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ } /* if */ sizeof_type = orig_sizeof_type; } else { /* It has been determined that the operand of the sizeof is an expression and not a type. Scan the operand. */ a_local_expr_options_set local_options = EOPT_NO_OPTIONS; if (is_parenthesized) local_options |= EOPT_TRAPPED_LEFT_PAREN; scan_expr(&operand, PREC_PREFIX, local_options); operand_was_created = TRUE; if (is_parenthesized) { /* When scanning the expression with a trapped left parenthesis, the position returned in the operand indicates the token following the left parenthesis, which is wrong. Correct it. */ copy_source_position(lparen_position, operand.position); } /* if */ #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = operand.end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* if */ } /* if */ /* The type or expression has been scanned (or retrieved from rcblock, on a rescan). */ if (is_type) { /* Type case. */ /* If the top type is a reference, drop the reference so that the sizeof applies to the type referenced. Keep a dependent reference. */ if (is_any_reference_type(sizeof_type) && !(is_template_dependent_context() && is_template_dependent_type(sizeof_type))) { sizeof_type = type_pointed_to(sizeof_type); } /* if */ } else { /* Expression case. */ /* Do not convert a type of "routine returning type" to "pointer to routine returning type". See section 3.2.2.1 in the C standard. Likewise do not convert arrays to pointers, or lvalues to rvalues. */ do_operand_transformations(&operand, TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION | TOPT_SUPPRESS_ARRAY_TO_POINTER_CONVERSION | TOPT_SUPPRESS_FUNCTION_TO_POINTER_CONVERSION | TOPT_SUPPRESS_MEMBER_FUNC_TO_PM_CONVERSION); if (is_bit_field_operand(&operand)) { /* This is a bit field; it is illegal except when in pcc compatibility mode. */ /* In pcc mode the size of a bit field is the size of the type of the bit field (e.g., "unsigned int"). */ if (C_dialect != C_dialect_pcc) { error_in_operand(ec_sizeof_bit_field, &operand); } /* if */ } /* if */ force_complete_type_if_a_variable(&operand); orig_sizeof_type = operand.type; sizeof_type = orig_sizeof_type; type_position = operand.position; if (operand_is_instantiation_dependent(&operand)) { template_case = TRUE; } /* if */ } /* if */ #if UPC_EXTENSIONS_ALLOWED if (upc_mode) { /* Determine if the multiplication by THREADS is needed before the typerefs are stripped off. */ multiply_by_threads_needed = (operator_token == tok_sizeof && is_underlying_threads_dimensioned_array_type(sizeof_type)); if (operator_token == tok_upc_blocksizeof) { /* Save the block size before stripping the typerefs. */ block_size = get_underlying_upc_block_size(sizeof_type); special_upc_size = block_size; if (block_size == UPC_BLOCK_SIZE_NONE) { /* For non-shared data we return a value greater than the maximum legal block size. */ if (is_pointer_type(sizeof_type) && is_underlying_shared_qualified_type( type_pointed_to(sizeof_type))) { expr_pos_warning(ec_nonshared_blocksizeof, &type_position); } /* if */ special_upc_size = max_upc_block_size + 1; } /* if */ use_special_upc_size = TRUE; } /* if */ } /* if */ #endif /* UPC_EXTENSIONS_ALLOWED */ /* Instantiate the type if it is a template class. */ complete_type_is_needed(sizeof_type); /* The operand of a sizeof may not have function type or incomplete type (except in GNU C mode, where function types and void are treated as byte-sized). */ if (!C_mode() && is_template_dependent_context() && is_template_dependent_type(sizeof_type)) { /* Don't test a template-dependent type. This is important, in particular, for some cases where, with implicit typename, disambiguation concludes that something is a function-type declaration instead of a functional-notation type conversion. */ template_case = TRUE; } else if (is_function_type(sizeof_type)) { if (gnu_mode) { /* GNU C/C++ evaluates sizeof(function-type) as 1. */ sizeof_type = integer_type((an_integer_kind)ik_char); is_type = TRUE; if (gpp_mode) { expr_pos_warning(ec_sizeof_function, &type_position); } /* if */ } else { expr_pos_error(ec_sizeof_function, &type_position); sizeof_type = error_type(); } /* if */ } else if (is_incomplete_type(sizeof_type) || (ms_version_is(>= 1900) && is_array_type(sizeof_type) && has_any_zero_bound(sizeof_type))) { /* MSVC appears to treat arrays with zero bounds as incomplete in this context. */ if (gnu_mode && is_void_type(sizeof_type)) { /* GNU C/C++ evaluates sizeof(void) as 1. */ if (gpp_mode) { expr_issue_incomplete_type_diag(&type_position, sizeof_type, /*severity=*/es_warning); } /* if */ sizeof_type = integer_type((an_integer_kind)ik_char); } else if (gpp_mode && gnu_version < 30400 && is_template_dependent_context() && !do_dependent_name_processing && is_class_struct_union_type(sizeof_type)) { /* g++ before 3.4 allows sizeof an incomplete class type in a prototype instantiation (because it doesn't instantiate the insides of templates). */ template_case = TRUE; } else { expr_issue_incomplete_type_diag(&type_position, sizeof_type); sizeof_type = error_type(); } /* if */ } else if (is_sizeless_type(sizeof_type)) { expr_issue_sizeless_type_error(&type_position, sizeof_type); sizeof_type = error_type(); } /* if */ /* Force building a template-dependent representation for cases that involve a dependent expression even though the result type is not dependent. This is done after the type validity tests above so we can detect any possible errors anyway. */ if (is_template_dependent_context() && is_instantiation_dependent_type(sizeof_type)) { template_case = TRUE; } /* if */ #if UPC_EXTENSIONS_ALLOWED if (upc_mode) { /* Special handling for VLAs. Not all operations are defined for them. */ if (vla_enabled && is_vla_type(sizeof_type)) { switch ((int)operator_token) { case tok_upc_localsizeof: /* Turn this into a sizeof. */ operator_token = tok_sizeof; break; case tok_upc_blocksizeof: /* Return as if it were indefinite block size. */ special_upc_size = UPC_BLOCK_SIZE_INDEFINITE; use_special_upc_size = TRUE; operator_token = tok_sizeof; break; default: /* Do nothing. */ break; } /* switch */ } /* if */ if (multiply_by_threads_needed && sizeof_itself_is_in_const_expr) { /* Not allowed in a constant expression. */ expr_pos_error(ec_expr_not_constant, &start_position); make_error_operand(result); err = TRUE; } else if (operator_token == tok_upc_localsizeof) { special_upc_size = (a_upc_block_size)upc_local_type_size(sizeof_type); use_special_upc_size = TRUE; } else if (operator_token == tok_upc_elemsizeof && is_array_type(sizeof_type)) { sizeof_type = underlying_array_element_type(sizeof_type); sizeof_type = skip_typerefs(sizeof_type); } /* if */ } /* if */ if (err) { /* Already handled */ } else if (use_special_upc_size) { set_unsigned_integer_constant(constant, /*lint --e(571) cast of signed to unsigned */ (a_host_large_unsigned)special_upc_size, targ_size_t_int_kind); make_constant_operand(constant, result); } else #endif /* UPC_EXTENSIONS_ALLOWED */ /* Do not add code here. */ if (vla_enabled && is_vla_type(sizeof_type) && !template_case) { /* One or more of the top array types is a variable-length array. */ if (sizeof_itself_is_in_const_expr) { /* Not allowed in a constant expression. */ expr_pos_error(ec_expr_not_constant, &start_position); make_error_operand(result); } else { /* Make an expression node to represent a sizeof that cannot be evaluated until runtime. */ (void)make_sizeof_expr(kind, is_type, orig_sizeof_type, &operand, result); operand_was_used = !is_type; nonconstant_case = TRUE; } /* if */ #if MICROSOFT_EXTENSIONS_ALLOWED } else if (cppcli_enabled && !template_case && (is_managed_class_type(sizeof_type) || is_handle_type(sizeof_type))) { /* In C++/CLI mode, the size of a value class type or a handle type is nonconstant, and applying sizeof to a ref/interface class type is invalid. (That's not the case in C++/CX mode though.) */ a_type_ptr tp = skip_typerefs(sizeof_type); if (is_immediate_class_type(tp) && (cli_class_type_kind_is(tp, cctk_ref) || cli_class_type_kind_is(tp, cctk_interface))) { expr_pos_error(ec_sizeof_ref_or_interface_class, &start_position); make_error_operand(result); } else if (sizeof_itself_is_in_const_expr) { expr_pos_error(ec_expr_not_constant, &start_position); make_error_operand(result); } else { /* Make an expression node to represent a sizeof that cannot be evaluated until runtime. */ (void)make_sizeof_expr(kind, is_type, orig_sizeof_type, &operand, result); operand_was_used = !is_type; nonconstant_case = TRUE; } /* if */ #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ #ifdef SIZEOF_TYPE_IS_UNKNOWN } else if (!template_case && SIZEOF_TYPE_IS_UNKNOWN(sizeof_type)) { /* The size of this type is not known at compile time. This is used with the C++-generating back end when it is difficult or impossible to duplicate the layout algorithm of the target compiler. Some types (e.g., int) might be easy to know, and some (e.g., non-POD classes) might not be. SIZEOF_TYPE_IS_UNKNOWN is a function-like macro that returns TRUE for the complicated cases. */ if (sizeof_itself_is_in_const_expr) { /* Not allowed in a constant expression. */ expr_pos_error(ec_expr_not_constant, &start_position); make_error_operand(result); } else { /* Make an expression node to represent the sizeof. */ (void)make_sizeof_expr(kind, is_type, orig_sizeof_type, &operand, result); operand_was_used = !is_type; nonconstant_case = TRUE; } /* if */ #endif /* defined(SIZEOF_TYPE_IS_UNKNOWN) */ } else { /* The result of a sizeof is an integer indicating the size of the operand in bytes, of type size_t (see ISO C 6.3.3.4 and ). */ if (is_error_type(sizeof_type)) { set_error_constant(constant); } else { if (template_case) { /* For the size of a template type, use a ck_template_param. */ a_template_param_constant_kind tpck = tpck_sizeof; if (kind == enk_datasizeof) tpck = tpck_datasizeof; clear_constant(constant, ck_template_param); set_template_param_constant_kind(constant, tpck); constant->variant.template_param.variant.templ_sizeof.type = sizeof_type; if (!is_type) { prep_generic_operand(&operand); constant->variant.template_param.variant.templ_sizeof.expr = make_node_from_operand(&operand); operand_was_used = TRUE; } /* if */ constant->type = integer_type(targ_size_t_int_kind); } else { a_host_large_unsigned size; /* Normal case; known constant sizeof. */ if (kind != enk_datasizeof) { size = (a_host_large_unsigned)size_of_type(sizeof_type); } else { size = (a_host_large_unsigned)data_size_of_type(sizeof_type); } /* if */ set_unsigned_integer_constant(constant, size, targ_size_t_int_kind); /* Make a sizeof expression that sits behind the constant and gives the original expression. If the expression contains a statement expression, this is required to avoid file-scope memory orphans (and potential IL write-read errors). */ if (curr_expr_kind_is_one_in_which_const_exprs_are_recorded() || curr_expr_has_gnu_statement_expression()) { switch_back_to_original_region(region_to_switch_back_to); if (!is_type && curr_il_region_number == file_scope_region_number && (innermost_function_scope != NULL || inside_local_class)) { /* An expression in a function scope might point to a local variable, which is in the function scope memory region. Therefore it cannot be attached to a file-scope constant. This comes up when a sizeof in an array bound uses a local variable in its expression. We suppress the recording of the expression in all cases, and just record the type. */ is_type = TRUE; } /* if */ constant->expr = make_sizeof_expr(kind, is_type, orig_sizeof_type, &operand, (an_operand *)NULL); operand_was_used = !is_type; switch_to_scope_region(depth_scope_stack, ®ion_to_switch_back_to); } /* if */ } /* if */ } /* if */ make_constant_operand(constant, result); } /* if */ #if UPC_EXTENSIONS_ALLOWED if (multiply_by_threads_needed && !err && !is_error_operand(result)) { /* Replace "result" by "result*THREADS". */ an_operand mo1, mo2; make_upc_thread_operand(&mo2, (a_constant_repr_kind)ck_upc_threads); copy_operand(result, &mo1); do_binary_operation((an_expr_operator_kind)eok_multiply, &mo1, &mo2, integer_type(targ_size_t_int_kind), result, &operator_position, NO_TOKEN_SEQUENCE_NUMBER); } /* if */ #endif /* UPC_EXTENSIONS_ALLOWED */ if (operand_was_created && !operand_was_used) { /* The expression was discarded. */ unevaluated_operand_dropped(&operand); } /* if */ set_operand_position(result, &start_position, &end_position, &operator_position); record_operator_position_in_rescan_info(result, &operator_position, NO_TOKEN_SEQUENCE_NUMBER, &type_position); pop_expr_stack(); if (cpp11_mode && curr_expr_kind_is_const() && nonconstant_case && construct_not_allowed_in_cpp11_constant_expr(ec_expr_not_constant, &start_position)) { /* Non-constant sizeof not allowed in a C++11 constant expression. */ conv_to_error_operand(result); } /* if */ switch_back_region_and_lifetime(region_to_switch_back_to, saved_object_lifetime); end_of_routine: release_local_constant(&constant); db_exit(); } /* scan_sizeof_operator */ static void scan_alignof_operator(a_rescan_control_block *rcblock, an_operand *result) /* Scan the alignof operator (including the C11 variant _Alignof, and extensions like __alignof__ or __ALIGNOF___). This is a feature that is similar to sizeof, but returns the alignment requirement rather than the size. Syntax: __alignof ( type-id ) __alignof expression where __alignof can be replaced by other applicable spelling. However, the second variant (with an expression operand) is not permitted in standard C (C11) or C++ (C++11). The current token is tok_alignof (for the standard syntax) or tok_ext_alignof. Scan a type or (if appropriate) an expression operand, and return an operand for alignof applied to that, in *result. If rcblock is non-NULL, redo semantic analysis on a previously-scanned alignof expression, and return the result in *result (or an error indication in *rcblock). Note that the __ALIGNOF__ and __alignof__ spelling variants (both tokenized as tok_ext_alignof) are accepted in all modes since this feature is often used in standard headers (e.g., to implement ). */ { a_source_position operator_position, start_position, type_position; a_source_position lparen_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ an_operand operand; a_constant_ptr constant = local_constant(); a_boolean is_parenthesized = FALSE, is_type = FALSE, is_std_syntax; a_type_ptr alignof_type = NULL; an_expr_stack_entry expr_stack_entry; a_boolean template_case, is_error; a_targ_alignment alignof_value; a_boolean operand_was_created = FALSE, operand_was_used = FALSE; a_memory_region_number region_to_switch_back_to; an_object_lifetime_ptr saved_object_lifetime; an_expr_node_ptr operand_expr = NULL; db_enter(4, "scan_alignof_operator"); if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. */ a_token_sequence_number operator_tok_seq_number; is_std_syntax = (rcblock->operator_token == tok_alignof); check_assertion(is_std_syntax || rcblock->operator_token == tok_ext_alignof); make_sizeof_et_al_rescan_operands(rcblock, &is_type, &operand, &alignof_type, &operator_position, &operator_tok_seq_number, &type_position); operand_was_created = !is_type; #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = rcblock->expr->expr_range.end; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } else { /* Normal, non-rescan, processing. */ is_std_syntax = (curr_token == tok_alignof); operator_position = pos_curr_token; } /* if */ start_position = operator_position; /* If we're in the file-scope memory region instead of a function-scope memory region because we're scanning something like an array bound, switch back. Any expression nodes allocated must be in the function-scope memory region. */ switch_to_scope_region_and_lifetime( scope_depth_to_allocate_unevaluated_operand(), ®ion_to_switch_back_to, &saved_object_lifetime); push_expr_stack_with_rcblock((an_expression_kind)ek_sizeof, &expr_stack_entry, /*force_object_lifetime=*/FALSE, /*suppress_object_lifetime=*/FALSE, rcblock); expr_stack->unevaluated_expr_will_be_kept_in_il = TRUE; check_for_c23_deprecation("_Alignof", ec_c23_alignof_deprecated); if (rcblock == NULL) { /* Advance past the __ALIGNOF__ token. */ (void)get_token(); if (curr_token == tok_lparen) { /* A left parenthesis could indicate a type in parentheses or an expression in parentheses, i.e., __ALIGNOF__ (int) vs. __ALIGNOF__ (i) We can distinguish the two using the first token inside the parentheses. However, if the construct is an expression in parentheses, we must then scan it with a special flag indicating that a left parenthesis was trapped. It's not enough to just scan the expression to the matching right parenthesis, as shown by the following: __ALIGNOF__ (v).b The __ALIGNOF__ should be applied to "(v).b", not just "(v)". */ is_parenthesized = TRUE; copy_source_position(pos_curr_token, lparen_position); (void)get_token(); if (is_decl_not_expr(DFS_ABSTRACT_DECLARATOR_ALLOWED | DFS_SINGLE_TYPE_REQUIRED)) { /* This is a type-name in parentheses. */ is_type = TRUE; } /* if */ } else if (ms_extensions) { /* Microsoft requires the parentheses even around an expression. */ expr_pos_diagnostic(es_discretionary_error, ec_exp_lparen, &pos_curr_token); } /* if */ if (is_type) { type_position = pos_curr_token; if (is_parenthesized) { /* Scan the type-name for a parenthesized type. */ add_matching_stop_token(tok_rparen); type_name(&alignof_type); #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ (void)required_token(tok_rparen, ec_exp_rparen); remove_matching_stop_token(tok_rparen); if (compound_literals_allowed && curr_token == tok_lbrace) { /* Something like __ALIGNOF__ (int){37} -- the type is the beginning of a compound literal. */ scan_compound_literal(&alignof_type, &lparen_position, &type_position, (a_rescan_control_block *)NULL, (an_init_component *)NULL, result, EOPT_NO_OPTIONS); alignof_type = result->type; } /* if */ } /* if */ } else { /* It has been determined that the operand of __ALIGNOF__ is an expression and not a type. Scan the operand. */ a_local_expr_options_set local_options = EOPT_NO_OPTIONS; if (is_parenthesized) local_options |= EOPT_TRAPPED_LEFT_PAREN; scan_expr(&operand, PREC_PREFIX, local_options); operand_was_created = TRUE; if (is_parenthesized) { /* When scanning the expression with a trapped left parenthesis, the position returned in the operand indicates the token following the left parenthesis, which is wrong. Correct it. */ copy_source_position(lparen_position, operand.position); } /* if */ #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = operand.end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* if */ } /* if */ /* The type or expression has been scanned (or retrieved from rcblock, on a rescan). */ if (is_type) { /* Type case. */ /* If the top type is a reference, drop the reference so that the operator applies to the type referenced. Keep a dependent reference. */ if (is_any_reference_type(alignof_type) && !(is_template_dependent_context() && is_template_dependent_type(alignof_type))) { alignof_type = type_pointed_to(alignof_type); } /* if */ } else { /* Expression case. */ if (is_std_syntax) { /* The standard C++11 "alignof" does not permit an expression argument. However, since common practice does permit it, we only issue a warning by default. */ expr_pos_diagnostic(strict_ansi_mode ? strict_ansi_discretionary_severity : es_warning, ec_std_alignof_with_expr_arg, &operand.position); } /* if */ /* Do not convert a type of "routine returning type" to "pointer to routine returning type". See section 3.2.2.1 in the C standard. Likewise do not convert arrays to pointers, or lvalues to rvalues. */ do_operand_transformations(&operand, TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION | TOPT_SUPPRESS_ARRAY_TO_POINTER_CONVERSION | TOPT_SUPPRESS_FUNCTION_TO_POINTER_CONVERSION | TOPT_SUPPRESS_MEMBER_FUNC_TO_PM_CONVERSION); force_complete_type_if_a_variable(&operand); alignof_type = operand.type; type_position = operand.position; if (is_expression_operand(&operand)) { operand_expr = skip_parens(operand.variant.expression); } /* if */ } /* if */ if (is_std_syntax) { if (is_function_type(alignof_type) && !(gnu_mode && !clang_mode)) { expr_pos_ty_diagnostic(es_discretionary_error, ec_alignof_function_type, &type_position, alignof_type); } else if (c11_mode && is_incomplete_array_type(alignof_type)) { an_error_severity sev = strict_ansi_mode ? strict_ansi_discretionary_severity : es_warning; pos_diagnostic(sev, ec_alignof_incomplete_array, &type_position); } /* if */ } /* if */ alignof_value = compute_alignof_value(alignof_type, is_type, operand_expr, &start_position, &is_error, &template_case); if (!is_type && !template_case && is_template_dependent_context() && operand_is_instantiation_dependent(&operand)) { template_case = TRUE; } /* if */ /* The result of alignof (or __ALIGNOF__, etc.) is an integer indicating the alignment of the operand, of type size_t. */ if (is_error) { set_error_constant(constant); } else if (template_case) { /* For the alignment of a template-dependent type, use a ck_template_param. */ clear_constant(constant, (a_constant_repr_kind)ck_template_param); set_template_param_constant_kind(constant, (a_template_param_constant_kind)tpck_alignof); constant->variant.template_param.variant.templ_sizeof.type = alignof_type; constant->variant.template_param.variant.templ_sizeof.is_std_alignof = is_std_syntax; if (!is_type) { prep_generic_operand(&operand); constant->variant.template_param.variant.templ_sizeof.expr = make_node_from_operand(&operand); operand_was_used = TRUE; } /* if */ constant->type = integer_type(targ_size_t_int_kind); } else { /* Normal case; known constant alignof. */ set_unsigned_integer_constant( constant, (a_host_large_unsigned)alignof_value, targ_size_t_int_kind); /* Make an alignof expression that sits behind the constant and gives the original expression. */ if (curr_expr_kind_is_one_in_which_const_exprs_are_recorded()) { switch_back_to_original_region(region_to_switch_back_to); if (!is_type && curr_il_region_number == file_scope_region_number && (innermost_function_scope != NULL || inside_local_class)) { /* An expression in a function scope might point to a local variable, which is in the function scope memory region. Therefore it cannot be attached to a file-scope constant. This comes up when an alignof in an array bound uses a local variable in its expression. We suppress the recording of the expression in all cases, and just record the type. */ is_type = TRUE; } /* if */ constant->expr = make_sizeof_expr(enk_alignof, is_type, alignof_type, &operand, (an_operand *)NULL); constant->expr->variant.sizeof_info.is_std_alignof = is_std_syntax; operand_was_used = !is_type; switch_to_scope_region(depth_scope_stack, ®ion_to_switch_back_to); } /* if */ } /* if */ make_constant_operand(constant, result); if (operand_was_created && !operand_was_used) { /* The expression was discarded. */ unevaluated_operand_dropped(&operand); } /* if */ set_operand_position(result, &start_position, &end_position, &operator_position); record_operator_position_in_rescan_info(result, &operator_position, NO_TOKEN_SEQUENCE_NUMBER, &type_position); pop_expr_stack(); switch_back_region_and_lifetime(region_to_switch_back_to, saved_object_lifetime); release_local_constant(&constant); db_exit(); } /* scan_alignof_operator */ static void record_position_in_expr_for_rescan( an_expr_node_ptr node, a_source_position *start_position, ARG_UNUSED a_source_position *end_position) /* Record the source starting and ending positions in the given expression so they will be available in a later rescan. This is used for unusual operations where an operator position is not needed, and forces creation of rescan information. */ { if (expr_stack->possible_rescan_context && !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); set_operand_position(&operand, start_position, end_position, &null_source_position); #if 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 /* !CHECKING */ (void)make_node_from_operand(&operand); #endif /* CHECKING */ } /* if */ } /* record_position_in_expr_for_rescan */ static void record_type_operand_position_for_rescan( an_expr_node_ptr node, a_source_position *start_position) /* Record a source position in a type operand (enk_type_operand node) if necessary for use in a later rescan. */ { /* This is kind of a weird routine in that it does not record an end position. It didn't seem worth the trouble to maintain it given that positions are basically not used in a rescan. */ if (node->kind == (an_expr_node_kind)enk_type_operand) { record_position_in_expr_for_rescan(node, start_position, &null_source_position); } else { check_assertion(is_error_node(node)); } /* if */ } /* record_type_operand_position_for_rescan */ static void scan_offsetof(a_rescan_control_block *rcblock, an_operand *result) /* Recent versions of the GNU compilers implement the offsetof macro using the __builtin_offsetof construct, which takes the general form: __builtin_offsetof ( , ) This routine assumes the current token is __builtin_offsetof, scans the construct, and either creates an integer constant operand or an enk_builtin_operation node to represent the operation. Much of the difficult work is done by scan_field_selection_operator and scan_subscript_operator. If rcblock is non-NULL, redo semantic analysis on a previously-scanned offsetof expression, and return the result in *result (or an error indication in *rcblock). */ { a_type_ptr type, utype; a_source_position type_position, start_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ a_boolean valid_type; an_operand operand, local_result; an_expr_node_ptr node, args, rescan_op2 = NULL; an_expr_stack_entry expr_stack_entry; if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. */ an_expr_node_ptr expr = rcblock->expr; an_expr_rescan_info_entry_ptr eriep; check_assertion(expr->kind == (an_expr_node_kind)enk_builtin_operation && expr->variant.builtin_operation.kind == (a_builtin_operation_kind)bok_offsetof); eriep = get_expr_rescan_info(expr, (an_expr_rescan_info_entry *)NULL); start_position = eriep->saved_operand.position; #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = expr->expr_range.end; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ rcblock->argument_list = expr->variant.builtin_operation.operands; /* Pick up the first operand, the type. */ make_type_operand_rescan_type(rcblock, &type, &type_position); rescan_op2 = rcblock->argument_list->next; } else { a_decl_parse_state dps; /* Normal, non-rescan, processing. */ start_position = pos_curr_token; /* Pass over the built-in offsetof token. */ check_assertion(curr_token == tok_builtin_offsetof); (void)get_token(); /* Check for and pass over the left parenthesis. */ (void)required_token(tok_lparen, ec_exp_lparen); type_position = pos_curr_token; add_stop_token(tok_rparen); init_decl_parse_state(&dps); type_name_full(&dps); if (!(gpp_mode && !clang_mode)) { check_type_definition_in_type_name(&dps); } /* if */ type = dps.type; } /* if */ utype = skip_typerefs(type); if (is_immediate_class_type(utype)) { /* Instantiate the type if it is a template class. */ complete_type_is_needed(utype); if (!C_mode() && is_nonPOD_or_has_non_standard_layout(utype) && !utype->variant.class_struct_union.is_nonreal_class) { expr_pos_warning(ec_offset_in_non_POD_nonstandard, &type_position); } /* if */ valid_type = TRUE; } else if (utype->kind == (a_type_kind)tk_template_param) { valid_type = TRUE; } else { expr_pos_error(ec_exp_class_type, &type_position); valid_type = FALSE; } /* if */ /* Synthesize a null operand representing an lvalue of the scanned type and call scan_field_selection_operator on that (with the comma token replaced by a period token). The construct may involve multilevel field or array element access (e.g., "__builtin_offsetof(a, x.y[3])"), so we iterate over field selection and/or array subscript operations as needed. */ push_expr_stack_with_rcblock((an_expression_kind)ek_normal, &expr_stack_entry, /*force_object_lifetime=*/FALSE, /*suppress_object_lifetime=*/TRUE, rcblock); expr_stack->unevaluated_expr_will_be_kept_in_il = TRUE; if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. The second operand contains all the field selections and subscripts and can be processed in a single call without a loop. */ make_rescan_operand_full(rescan_op2, rcblock, EOPT_OPERAND_OF_OFFSETOF, &local_result, (an_operand *)NULL); } else { /* Scanning from source. */ /* Check for the comma. */ if (curr_token != tok_comma) { expr_syntax_error(ec_exp_comma); make_error_operand(&local_result); } else { /* Make an lvalue of the right type to start the tree. */ make_dummy_lvalue_operand(type, &local_result); curr_token = tok_period; do { /* Loop, picking up a field selection or a subscript operation each time around. */ copy_operand(&local_result, &operand); if (curr_token == tok_period) { scan_field_selection_operator(&operand, (a_rescan_control_block *)NULL, /*call_rescan_case=*/FALSE, /*offsetof_case=*/TRUE, &local_result, (an_operand*)NULL); } else { scan_subscript_operator(&operand, /*offsetof_case=*/TRUE, (a_rescan_control_block *)NULL, &local_result); } /* if */ } while (curr_token == tok_period || curr_token == tok_lbracket); } /* if */ #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ } /* if */ if (valid_type && !is_error_operand(&local_result)) { a_constant_ptr offset_constant = local_constant(); a_boolean nonconstant_offset; /* Build the first operand as a type node. */ args = alloc_expr_node((an_expr_node_kind)enk_type_operand); args->type = void_type(); args->variant.type_operand.type = type; record_type_operand_position_for_rescan(args, &type_position); /* The second operand is the selection expression. */ args->next = make_node_from_operand(&local_result); /* Finally, create the node representing the offsetof operation, and fold it into a constant if possible. */ node = alloc_expr_node((an_expr_node_kind)enk_builtin_operation); node->type = integer_type(targ_size_t_int_kind); node->variant.builtin_operation.kind = (a_builtin_operation_kind)bok_offsetof; node->variant.builtin_operation.operands = args; fold_builtin_operation_if_possible( node, offset_constant, curr_expr_kind_is_one_in_which_const_exprs_are_recorded(), &start_position, &nonconstant_offset); if (nonconstant_offset) { /* The offset is not a constant. */ make_expression_operand(node, result); } else { /* The offset is a (possibly template-dependent) constant. */ make_constant_operand(offset_constant, result); result->type = result->variant.constant.type; } /* if */ release_local_constant(&offset_constant); } else { make_error_operand(result); operand_will_not_be_used_because_of_error(&local_result); } /* if */ set_operand_position(result, &start_position, &end_position, &start_position); record_operator_position_in_rescan_info(result, &start_position, NO_TOKEN_SEQUENCE_NUMBER, &type_position); pop_expr_stack(); if (rcblock == NULL) { remove_stop_token(tok_rparen); /* Check for and pass over the right parenthesis. */ (void)required_token(tok_rparen, ec_exp_rparen); } /* if */ } /* scan_offsetof */ static void scan_builtin_addressof(a_rescan_control_block *rcblock, an_operand *result) /* Scan the __builtin_addressof construct, which takes the general form: __builtin_addressof ( ) This routine assumes the current token is __builtin_addressof, scans the construct, and either creates an address constant operand or a builtin function call. If rcblock is non-NULL, redo semantic analysis on a previously-scanned __builtin_addressof expression, and return the result in *result (or an error indication in *rcblock). */ { a_source_position start_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ an_operand operand; a_boolean err = FALSE; a_token_sequence_number start_tok_seq_number; if (rcblock != NULL) { /* Redoing semantic analysis on a previously-scanned expression. */ check_assertion(rcblock->operator_token == tok_ampersand); make_rescan_operands(rcblock, &operand, (an_operand *)NULL, (an_operand *)NULL, &start_position, &start_tok_seq_number, (a_source_position *)NULL); } else { a_decl_parse_state dps; /* Normal, non-rescan, processing. */ start_position = pos_curr_token; /* Pass over the __builtin_addressof token. */ check_assertion(curr_token == tok_builtin_addressof); (void)get_token(); /* Check for and pass over the left parenthesis. */ (void)required_token(tok_lparen, ec_exp_lparen); add_stop_token(tok_rparen); init_decl_parse_state(&dps); scan_expr(&operand, PREC_LOWEST, EOPT_DISALLOW_COMMA_OPERATOR); #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ if (!is_template_dependent_context() && (is_a_prvalue(&operand) || is_an_xvalue(&operand))) { /* It appears that clang doesn't allow xvalues in this case. */ error_in_operand(ec_expr_not_an_lvalue_or_function_designator, &operand); } /* if */ } /* if */ if (is_error_operand(&operand)) { err = TRUE; } else { /* Use the same operand transformations as are found in scan_ampersand_operator. */ do_operand_transformations(&operand, TOPT_SUPPRESS_LVALUE_TO_RVALUE_CONVERSION | TOPT_SUPPRESS_ARRAY_TO_POINTER_CONVERSION | TOPT_SUPPRESS_FUNCTION_TO_POINTER_CONVERSION | TOPT_SUPPRESS_MEMBER_FUNC_TO_PM_CONVERSION | TOPT_SUPPRESS_CHECK_FOR_INDEFINITE_FUNCTION); take_address_of_or_reference_to_lvalue(&operand, /*reference_case=*/FALSE, /*rvalue_reference_case=*/FALSE, /*use_handle_for_ref_class=*/FALSE, /*is_builtin_addressof=*/TRUE, &start_position); if (is_constant_operand(&operand)) { /* In some cases the operand is created as a constant. */ copy_operand(&operand, result); } else { /* If the operand has not been folded to a constant, try to fold it now. */ a_constant_ptr addressof_constant = local_constant(); a_boolean not_a_constant; fold_builtin_operation_if_possible( make_node_from_operand(&operand), addressof_constant, curr_expr_kind_is_one_in_which_const_exprs_are_recorded(), &start_position, ¬_a_constant); if (not_a_constant) { /* Failed to fold. */ copy_operand(&operand, result); } else { /* Folded to a (possibly template-dependent) constant. */ make_constant_operand(addressof_constant, result); result->type = result->variant.constant.type; } /* if */ release_local_constant(&addressof_constant); } /* if */ } /* if */ if (err) { make_error_operand(result); operand_will_not_be_used_because_of_error(&operand); } /* if */ set_operand_position(result, &start_position, &end_position, &start_position); record_operator_position_in_rescan_info(result, &start_position, NO_TOKEN_SEQUENCE_NUMBER, (a_source_position *)NULL); if (rcblock == NULL) { remove_stop_token(tok_rparen); /* Check for and pass over the right parenthesis. */ (void)required_token(tok_rparen, ec_exp_rparen); } /* if */ } /* scan_builtin_addressof */ /* Enumeration used when scanning various arguments to builtin operations. Describes the various argument types that can occur. */ enum a_builtin_arg_kind { bak_none, /* Used to indicate no argument. */ bak_type, /* A type argument is expected. */ bak_template, /* A template argument is expected. */ bak_any_expr, /* An expression (possibly an lvalue) is expected. */ bak_prvalue /* A prvalue expression is expected. */ }; static an_expr_node_ptr scan_builtin_operation_arg( a_rescan_control_block *rcblock, a_builtin_arg_kind arg_kind) /* Scan a single argument of a constant-expression of the form operation-name ( ) arg_kind specifies the kind of argument that is to be scanned (see the description of a_builtin_arg_kind for the various kinds). If rcblock is non-NULL, redo semantic analysis on a previously-scanned argument given by rcblock->argument_list. */ { an_expr_node_ptr result = NULL; a_source_position start_position; if (rcblock != NULL) { check_assertion(rcblock->argument_list != NULL); } else { add_stop_token(tok_comma); } /* if */ switch (arg_kind) { case bak_type: { a_type_ptr type; if (rcblock != NULL) { /* Get the type by doing substitution on the previously-scanned type. */ make_type_operand_rescan_type(rcblock, &type, &start_position); } else { /* Scan the type from source. */ a_decl_parse_state dps; start_position = pos_curr_token; init_decl_parse_state(&dps) if (!clang_version_is(<190000)) { /* Most implementations appear to accept the same type-id forms as would be accepted as a template argument. In particular, that includes types specific to member functions, such as "int() &". (Early versions of Clang did not accept that, however.) */ dps.is_template_type_argument = TRUE; } /* if */ type_name_full(&dps); check_type_definition_in_type_name(&dps); type = dps.type; } /* if */ if (is_error_type(type)) { result = alloc_expr_node((an_expr_node_kind)enk_error); } else { result = alloc_expr_node((an_expr_node_kind)enk_type_operand); result->type = void_type(); result->variant.type_operand.type = type; } /* if */ result->position = start_position; record_type_operand_position_for_rescan(result, &start_position); } break; case bak_template: { a_template_ptr templ = NULL; if (rcblock != NULL) { /* Get the template by doing substitution on the previously-scanned template name. */ make_template_name_rescan_template(rcblock, &templ, &start_position); } else { /* Scan the template name from source. */ start_position = pos_curr_token; if (is_generalized_identifier_start(GID_CLASS_TEMPLATE_REQUIRED)) { a_boolean err = FALSE; a_symbol_ptr sym = coalesce_and_lookup_generalized_identifier( GID_CLASS_TEMPLATE_REQUIRED, ilm_normal, &err); if (err) { syntax_error(ec_exp_identifier); } else if (!is_class_template_symbol(sym)) { pos_sy_error(ec_sym_not_a_class_template, &start_position, sym); } else { sym = template_argument_if_template_template_param(sym); templ = sym->variant.template_info->il_template_entry; } /* if */ (void)get_token(); } else { syntax_error(ec_exp_identifier); } /* if */ } /* if */ if (templ == NULL) { result = alloc_expr_node(enk_error); } else { result = alloc_expr_node(enk_template_name); result->type = void_type(); result->variant.template_name = templ; result->position = start_position; record_position_in_expr_for_rescan(result, &start_position, &null_source_position); } /* if */ } break; case bak_prvalue: case bak_any_expr: { an_operand operand; if (rcblock != NULL) { /* Expression case. Rescan the operand. */ an_expr_node_ptr op_expr = rcblock->expr->variant.builtin_operation.operands->next->next; make_rescan_operand(op_expr, rcblock, &operand); } else { a_local_expr_options_set local_options = EOPT_NO_OPTIONS; scan_expr(&operand, PREC_PREFIX, local_options); } /* if */ if (arg_kind == bak_prvalue) { /* Transform into a prvalue. */ do_operand_transformations(&operand, TOPT_NO_OPTIONS); } /* if */ result = make_node_from_operand(&operand); } break; default: unexpected_condition(); } /* switch */ if (rcblock != NULL) { /* Advance to the next argument for the next time around. */ rcblock->argument_list = rcblock->argument_list->next; } else { remove_stop_token(tok_comma); } /* if */ return result; } /* scan_builtin_operation_arg */ static void scan_call_like_builtin_operation( a_rescan_control_block *rcblock, a_builtin_operation_kind kind, a_type_ptr type, a_builtin_arg_kind arg1_kind, a_builtin_arg_kind arg2_kind, a_boolean arg2_repeats, an_operand *result) /* Scan an operation of the general form operation-name ( ) with one or more arguments as described by arg1_kind and arg2_kind. See the definition of a_builtin_arg_kind for valid values for arguments. arg1_kind (which cannot be bak_none) describes the first argument to be scanned. arg2_kind (which may be bak_none) describes the second argument to be scanned. If arg2_repeats is TRUE, additional arguments (as described by arg2_kind) are scanned until a closing parenthesis is found. An exception is made for bok_is_corresponding_member where a total of four arguments are scanned (the third and fourth arguments are scanned as bak_prvalues). Typically, the result of the operation is folded into a constant operand of the given type in *result, but in certain cases (i.e., when the folding operation returns not_a_constant == TRUE), the operation will not be folded and the *result operation will contain an expression for the builtin call. If rcblock is non-NULL, redo semantic analysis on a previously-scanned builtin operation expression, and return the result in *result (or an error indication in *rcblock). */ { a_boolean err = FALSE; a_source_position start_position; #if EXTRA_SOURCE_POSITIONS_IN_IL a_source_position end_position; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ an_expr_node_ptr arg1 = NULL, arg2; /* Currently, only type arguments are implemented. I.e., argX_kind must be iek_none or iek_type. */ if (rcblock != NULL) { an_expr_node_ptr expr = rcblock->expr; an_expr_rescan_info_entry_ptr eriep; check_assertion(expr->kind == (an_expr_node_kind)enk_builtin_operation && expr->variant.builtin_operation.kind == (a_builtin_operation_kind)kind); eriep = get_expr_rescan_info(expr, (an_expr_rescan_info_entry *)NULL); start_position = eriep->operator_position; #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = expr->expr_range.end; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ rcblock->argument_list = expr->variant.builtin_operation.operands; } else { start_position = pos_curr_token; /* Pass over the operation name. */ (void)get_token(); /* Check for and pass over the left parenthesis. */ (void)required_token(tok_lparen, ec_exp_lparen); add_matching_stop_token(tok_rparen); } /* if */ check_assertion(arg1_kind != bak_none); arg1 = scan_builtin_operation_arg(rcblock, arg1_kind); err |= (int)is_error_node(arg1); if (arg2_kind != bak_none) { if (arg2_repeats) { /* A (possibly empty) list of arguments after the first. */ an_expr_node_ptr argn, last_arg = arg1; a_boolean any_more; an_expr_rescan_info_entry_ptr eriep; a_pack_expansion_descr_ptr pedep; a_pack_expansion_stack_entry_ptr pesep; if (rcblock != NULL) { /* Rescanning. */ an_expr_node_ptr arg_expr = rcblock->argument_list; for (; arg_expr != NULL && !err; arg_expr = arg_expr->next) { if (arg_expr->is_pack_expansion) { eriep = get_expr_rescan_info(arg_expr, (an_expr_rescan_info_entry *)NULL); pedep = eriep->saved_operand.pack_expansion_descr; check_assertion(pedep != NULL); any_more = begin_rescan_pack_expansion_context(pedep, rcblock->template_param_list, rcblock->template_arg_list, &pesep, rcblock->options, rcblock->ctws_state, &err); while (any_more) { /* Rescan each member of the pack expansion. */ rcblock->argument_list = arg_expr; argn = scan_builtin_operation_arg(rcblock, arg2_kind); err |= (int)is_error_node(argn); last_arg->next = argn; last_arg = argn; (void)end_potential_pack_expansion_context( pesep, /*is_declarator=*/FALSE); any_more = advance_to_next_pack_element(pesep); } /* while */ } else { /* Not a pack expansion. Rescan one argument. */ argn = scan_builtin_operation_arg(rcblock, arg2_kind); err |= (int)is_error_node(argn); last_arg->next = argn; last_arg = argn; } /* if */ } /* while */ } else { /* Scanning from source or a token cache. */ while (curr_token == tok_comma) { (void)required_token(tok_comma, ec_exp_comma); any_more = begin_potential_pack_expansion_context(&pesep); while (any_more) { argn = scan_builtin_operation_arg(rcblock, arg2_kind); err |= (int)is_error_node(argn); last_arg->next = argn; last_arg = argn; pedep = end_potential_pack_expansion_context( pesep, /*is_declarator=*/FALSE); if (pedep != NULL && !is_error_node(argn)) { /* This element is a variadic template pack expansion, i.e., it's followed by "...". Furthermore, we're in the prototype instantiation, so we record the expansion information on the element. */ argn->is_pack_expansion = TRUE; if (expr_stack->possible_rescan_context) { an_expr_rescan_info_entry_ptr rescan_info = argn->extra.rescan_info; check_assertion(rescan_info != NULL); rescan_info->saved_operand.pack_expansion_descr = pedep; } /* if */ } /* if */ any_more = advance_to_next_pack_element(pesep); } /* while */ } /* while */ } /* if */ } else { /* Two arguments, no repeat. */ if (rcblock == NULL) (void)required_token(tok_comma, ec_exp_comma); arg2 = scan_builtin_operation_arg(rcblock, arg2_kind); err |= (int)is_error_node(arg2); arg1->next = arg2; if (kind == bok_is_corresponding_member) { /* Scan two additional expression arguments. */ if (rcblock == NULL) (void)required_token(tok_comma, ec_exp_comma); arg2->next = scan_builtin_operation_arg(rcblock, bak_prvalue); err |= (int)is_error_node(arg2->next); if (rcblock == NULL) (void)required_token(tok_comma, ec_exp_comma); arg2->next->next = scan_builtin_operation_arg(rcblock, bak_prvalue); err |= (int)is_error_node(arg2->next->next); } /* if */ } /* if */ } /* if */ if (!err) { /* Create the constant result operand by attempting to fold an enk_builtin_operation node that represents the operation. */ an_expr_node_ptr expr; a_boolean not_a_constant; expr = alloc_expr_node((an_expr_node_kind)enk_builtin_operation); expr->type = type; expr->position = start_position; expr->variant.builtin_operation.kind = (a_builtin_operation_kind)kind; expr->variant.builtin_operation.operands = arg1; record_operator_position_in_expr_rescan_info(expr, &start_position, NO_TOKEN_SEQUENCE_NUMBER, (a_source_position *)NULL); clear_operand((an_operand_kind)ok_constant, result); fold_builtin_operation_if_possible( expr, &result->variant.constant, curr_expr_kind_is_one_in_which_const_exprs_are_recorded(), &start_position, ¬_a_constant); if (!not_a_constant) { /* Typical case: the result is a constant. */ result->type = result->variant.constant.type; result->state = (an_operand_state)os_prvalue; } else { /* Couldn't coerce the builtin invocation into a constant, return the builtin expression (it might be able to be interpreted later). */ make_expression_operand(expr, result); } /* if */ } else { make_error_operand(result); if (rcblock != NULL) subst_fail(rcblock->error_detected); } /* if */ if (rcblock == NULL) { #if EXTRA_SOURCE_POSITIONS_IN_IL end_position = end_pos_curr_token; #endif /* EXTRA_SOURCE_POSITIONS_IN_IL */ remove_matching_stop_token(tok_rparen); /* Check for and pass over the right parenthesis. */ (void)required_token(tok_rparen, ec_exp_rparen); } /* if */ set_operand_position(result, &start_position, &end_position, &start_position); } /* scan_call_like_builtin_operation */ static a_type_ptr type_traits_helper_check(a_builtin_operation_kind kind) /* Issue an error if type traits helpers are not enabled. Returns the result type for the operation. kind specifies the builtin operation kind being checked (and is used only for the error message). */ { a_type_ptr result; if (!type_traits_helpers_enabled) { /* Type traits helpers are not accepted in some modes. */ if (expr_error_should_be_issued()) { pos_st_error(ec_feature_not_allowed_in_current_mode, &pos_curr_token, builtin_operation_names[(int)kind]); } /* if */ result = boolean_result_type(); } else { result = bool_type(); } /* if */ return result; } /* type_traits_helper_check */ static void scan_is_invocable(a_builtin_operation_kind kind, a_rescan_control_block *rcblock, an_operand *result) /* Scan a constant-expression having one of the following forms: __is_invocable( F , Args... ) __is_nothrow_invocable( F , Args... ) The result is of boolean type and true if a value of type F is "invocable" with arguments obtained with std::declval(). (Being "invocable" is defined in [func.require] in N4971, via the INVOKE operation.) */ { a_type_ptr result_type; result_type = type_traits_helper_check(kind); scan_call_like_builtin_operation(rcblock, kind, result_type, bak_type, bak_type, /*arg2_repeats=*/TRUE, result); if (!type_traits_helpers_enabled) { /* Turn the operand into an error operand to avoid any surprises later on. */ conv_to_error_operand(result); } /* if */ } /* scan_is_invocable */ static void scan_is_constructible(a_builtin_operation_kind kind, a_rescan_control_block *rcblock, an_operand *result) /* Scan a constant-expression having one of the following forms: __is_constructible( T , Args... ) __is_nothrow_constructible( T , Args... ) __is_trivially_constructible( T , Args... ) The result is a boolean of value true if the following variable definition would be well-formed for some invented variable t: T t(create()...); with template typename add_rvalue_reference::type create(); If kind is bok_is_nothrow_constructible, the definition must be known not to throw any exceptions. If kind is bok_is_trivially_constructible, the definition must be known not to involve a non-trivial (copy) construction. If rcblock is non-NULL, redo semantic analysis on a previously-scanned __is_constructible/__is_nothrow_constructible/__is_trivially_constructible expression, and return the result in *result (or an error indication in *rcblock). */ { a_type_ptr result_type; result_type = type_traits_helper_check(kind); scan_call_like_builtin_operation(rcblock, kind, result_type, bak_type, bak_type, /*arg2_repeats=*/TRUE, result); if (!type_traits_helpers_enabled) { /* Turn the operand into an error operand to avoid any surprises later on. */ conv_to_error_operand(result); } /* if */ } /* scan_is_constructible */ static void scan_is_destructible(a_builtin_operation_kind kind, a_rescan_control_block *rcblock, an_operand *result) /* Scan a constant-expression having one of the following forms: __is_destructible( T ) __is_nothrow_destructible( T ) __is_trivially_destructible( T ) The result is a boolean of value true if declval().~U() is well-formed, where U is T minus any top-level "array of" layers and the declval template is declared as follows: template typename add_rvalue_reference::type declval() noexcept; In the case of __is_nothrow_destructible, a true result also means it is known that no exception is thrown for the destruction, and in the case of __is_trivially_destructible the destructor is trivial. If rcblock is non-NULL, redo semantic analysis on a previously-scanned __is_destructible/__is_nothrow_destructible/__is_trivially_destructible expression, and return the result in *result (or an error indication in *rcblock). */ { a_type_ptr result_type; result_type = type_traits_helper_check(kind); scan_call_like_builtin_operation(rcblock, kind, result_type, bak_type, bak_none, /*arg2_repeats=*/FALSE, result); if (!type_traits_helpers_enabled) { /* Turn the operand into an error operand to avoid any surprises later on. */ conv_to_error_operand(result); } /* if */ } /* scan_is_destructible */ static void scan_is_valid_winrt_type(a_builtin_operation_kind kind, a_rescan_control_block *rcblock, an_operand *result) /* Scan a constant-expression of the form __is_valid_winrt_type (T) where T denotes a type. */ { a_type_ptr result_type; result_type = type_traits_helper_check(kind); scan_call_like_builtin_operation(rcblock, kind, result_type, bak_type, bak_none, /*arg2_repeats=*/FALSE, result); if (!type_traits_helpers_enabled) { /* Turn the operand into an error operand to avoid any surprises later on. */ conv_to_error_operand(result); } /* if */ } /* scan_is_valid_winrt_type */ static void scan_is_assignable(a_builtin_operation_kind kind, a_rescan_control_block *rcblock, an_operand *result) /* Scan a constant-expression having one of the following forms: __is_assignable( T , U ) __is_nothrow_assignable( T , U ) __is_trivially_assignable( T , U ) The result is a boolean of value true if declval() = declval() with template typename add_rvalue_reference::type declval() noexcept; is well-formed, and it is known no exception is thrown for the assignment (for the __is_nothrow_assignable case) or all calls involved are to trivial special members (in the __is_trivially_assignable case). If rcblock is non-NULL, redo semantic analysis on a previously-scanned __is_*assignable expression, and return the result in *result (or an error indication in *rcblock). */ { a_type_ptr result_type; result_type = type_traits_helper_check(kind); scan_call_like_builtin_operation(rcblock, kind, result_type, bak_type, bak_type, /*arg2_repeats=*/FALSE, result); if (!type_traits_helpers_enabled) { /* Turn the operand into an error operand to avoid any surprises later on. */ conv_to_error_operand(result); } /* if */ } /* scan_is_assignable */ static void scan_binary_type_trait_helper(a_rescan_control_block *rcblock, an_operand *result) /* Scan a constant-expression of the form __trait_keyword( , ) The result is a boolean of value true if the predicate corresponding to the __trait_keyword (e.g., "__is_convertible_to") applies to the operand types ( and ). If rcblock is non-NULL, redo semantic analysis on a previously-scanned construct of this kind. Either way, return the result in *result (or an error indication in *rcblock). */ { a_type_ptr result_type; a_builtin_operation_kind bok = bok_last; if (rcblock != NULL) { an_expr_node_ptr expr = rcblock->expr; check_assertion(expr != NULL && expr->kind == (an_expr_node_kind)enk_builtin_operation); bok = expr->variant.builtin_operation.kind; } else { switch (curr_token) { case tok_is_same: bok = bok_is_same; break; case tok_is_same_as: bok = bok_is_same_as; break; case tok_is_base_of: bok = bok_is_base_of; break; case tok_is_convertible_to: bok = bok_is_convertible_to; break; case tok_is_convertible: bok = bok_is_convertible; break; case tok_is_nothrow_convertible: bok = bok_is_nothrow_convertible; break; case tok_reference_binds_to_temporary: bok = bok_reference_binds_to_temporary; break; case tok_reference_constructs_from_temporary: bok = bok_reference_constructs_from_temporary; break; case tok_reference_converts_from_temporary: bok = bok_reference_converts_from_temporary; break; case tok_is_layout_compatible: bok = bok_is_layout_compatible; break; case tok_is_pointer_interconvertible_base_of: bok = bok_is_pointer_interconvertible_base_of; break; case tok_is_pointer_interconvertible_with_class: bok = bok_is_pointer_interconvertible_with_class; break; case tok_builtin_is_virtual_base_of: bok = bok_builtin_is_virtual_base_of; break; case tok_builtin_lt_synthesizes_from_spaceship: bok = bok_builtin_lt_synthesizes_from_spaceship; break; case tok_builtin_gt_synthesizes_from_spaceship: bok = bok_builtin_gt_synthesizes_from_spaceship; break; case tok_builtin_le_synthesizes_from_spaceship: bok = bok_builtin_le_synthesizes_from_spaceship; break; case tok_builtin_ge_synthesizes_from_spaceship: bok = bok_builtin_ge_synthesizes_from_spaceship; break; default: unexpected_condition(); } /* switch */ } /* if */ result_type = type_traits_helper_check(bok); scan_call_like_builtin_operation(rcblock, bok, result_type, bak_type, bak_type, /*arg2_repeats=*/FALSE, result); if (!type_traits_helpers_enabled) { /* Turn the operand into an error operand to avoid any surprises later on. */ conv_to_error_operand(result); } /* if */ } /* scan_binary_type_trait_helper */ static void scan_is_pointer_interconvertible_with_class( a_rescan_control_block *rcblock, an_operand *result) /* Scan a call to __is_pointer_interconvertible_with_class (MS): __is_pointer_interconvertible_with_class( , ) or (GCC): __builtin_is_pointer_interconvertible_with_class( ) Note that the operation may not be able to be folded here (e.g., because the ptr-to-member argument is a variable and not a constant). In that case, the interpreter may be able to "fold" the expression later. */ { a_type_ptr result_type; a_builtin_operation_kind op; if (rcblock != NULL) { an_expr_node_ptr expr = rcblock->expr; check_assertion(expr != NULL && expr->kind == enk_builtin_operation); op = expr->variant.builtin_operation.kind; } else { if (curr_token == tok_is_pointer_interconvertible_with_class) { op = bok_is_pointer_interconvertible_with_class; } else { op = bok_builtin_is_pointer_interconvertible_with_class; } /* if */ } /* if */ check_assertion(op == bok_is_pointer_interconvertible_with_class || op == bok_builtin_is_pointer_interconvertible_with_class); result_type = type_traits_helper_check(op); if (op == bok_is_pointer_interconvertible_with_class) { scan_call_like_builtin_operation(rcblock, op, result_type, bak_type, bak_prvalue, /*arg2_repeats=*/FALSE, result); } else { scan_call_like_builtin_operation(rcblock, op, result_type, bak_prvalue, bak_none, /*arg2_repeats=*/FALSE, result); } /* if */ if (!type_traits_helpers_enabled) { /* Turn the operand into an error operand to avoid any surprises later on. */ conv_to_error_operand(result); } /* if */ } /* scan_is_pointer_interconvertible_with_class */ static void scan_is_corresponding_member(a_rescan_control_block *rcblock, an_operand *result) /* Scan a call to __is_corresponding_member (MS): __is_corresponding_member( , , , ) or (GCC): __builtin_is_corresponding_member( , ) Note that the operation may not be able to be folded here (e.g., because the pointer-to-member arguments may be a variable and not a constant). In that case, the interpreter may be able to "fold" the expression later. */ { a_type_ptr result_type; a_builtin_operation_kind op; if (rcblock != NULL) { an_expr_node_ptr expr = rcblock->expr; check_assertion(expr != NULL && expr->kind == enk_builtin_operation); op = expr->variant.builtin_operation.kind; } else { if (curr_token == tok_is_corresponding_member) { op = bok_is_corresponding_member; } else { op = bok_builtin_is_corresponding_member; } /* if */ } /* if */ check_assertion(op == bok_is_corresponding_member || op == bok_builtin_is_corresponding_member); result_type = type_traits_helper_check(op); if (op == bok_is_corresponding_member) { /* Note that this builtin takes four arguments and there is special code in scan_call_like_builtin_operation to handle the third and fourth arguments. */ scan_call_like_builtin_operation(rcblock, op, result_type, bak_type, bak_type, /*arg2_repeats=*/FALSE, result); } else { scan_call_like_builtin_operation(rcblock, op, result_type, bak_prvalue, bak_prvalue, /*arg2_repeats=*/FALSE, result); } /* if */ if (!type_traits_helpers_enabled) { /* Turn the operand into an error operand to avoid any surprises later on. */ conv_to_error_operand(result); } /* if */ } /* scan_is_corresponding_member */ static void scan_edg_is_deducible(a_rescan_control_block *rcblock, an_operand *result) /* Scan a call to __edg_is_deducible: __edg_is_deducible(