| // Copyright (C) 2020-2023 Free Software Foundation, Inc. |
| |
| // This file is part of GCC. |
| |
| // GCC is free software; you can redistribute it and/or modify it under |
| // the terms of the GNU General Public License as published by the Free |
| // Software Foundation; either version 3, or (at your option) any later |
| // version. |
| |
| // GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| // WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| // for more details. |
| |
| // You should have received a copy of the GNU General Public License |
| // along with GCC; see the file COPYING3. If not see |
| // <http://www.gnu.org/licenses/>. |
| |
| #include "rust-tree.h" |
| #include "fold-const.h" |
| #include "stringpool.h" |
| #include "attribs.h" |
| #include "escaped_string.h" |
| #include "libiberty.h" |
| #include "stor-layout.h" |
| #include "hash-map.h" |
| #include "diagnostic.h" |
| #include "timevar.h" |
| #include "convert.h" |
| #include "gimple-expr.h" |
| #include "gimplify.h" |
| #include "function.h" |
| #include "gcc-rich-location.h" |
| #include "target.h" |
| #include "file-prefix-map.h" |
| #include "cgraph.h" |
| #include "output.h" |
| #include "memmodel.h" |
| #include "tm_p.h" |
| |
| // forked from gcc/c-family/c-common.cc c_global_trees |
| tree c_global_trees[CTI_MAX]; |
| // forked from gcc/cp/decl.cc cp_global_trees |
| tree cp_global_trees[CPTI_MAX]; |
| |
| struct saved_scope *scope_chain; |
| |
| namespace Rust { |
| |
| void |
| mark_exp_read (tree exp) |
| { |
| char tmp_name[32]; |
| ASM_GENERATE_INTERNAL_LABEL (tmp_name, "Lsrc_loc", 1); |
| |
| if (exp == NULL) |
| return; |
| |
| switch (TREE_CODE (exp)) |
| { |
| case VAR_DECL: |
| gcc_fallthrough (); |
| case PARM_DECL: |
| DECL_READ_P (exp) = 1; |
| break; |
| case ARRAY_REF: |
| case COMPONENT_REF: |
| case MODIFY_EXPR: |
| case REALPART_EXPR: |
| case IMAGPART_EXPR: |
| CASE_CONVERT: |
| case ADDR_EXPR: |
| case INDIRECT_REF: |
| case FLOAT_EXPR: |
| case NON_DEPENDENT_EXPR: |
| case VIEW_CONVERT_EXPR: |
| mark_exp_read (TREE_OPERAND (exp, 0)); |
| break; |
| case COMPOUND_EXPR: |
| mark_exp_read (TREE_OPERAND (exp, 1)); |
| break; |
| case COND_EXPR: |
| if (TREE_OPERAND (exp, 1)) |
| mark_exp_read (TREE_OPERAND (exp, 1)); |
| if (TREE_OPERAND (exp, 2)) |
| mark_exp_read (TREE_OPERAND (exp, 2)); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| tree |
| convert_from_reference (tree val) |
| { |
| if (TREE_TYPE (val) && TYPE_REF_P (TREE_TYPE (val))) |
| { |
| tree t = TREE_TYPE (TREE_TYPE (val)); |
| tree ref = build1 (INDIRECT_REF, t, val); |
| |
| mark_exp_read (val); |
| |
| TREE_SIDE_EFFECTS (ref) |
| = (TREE_THIS_VOLATILE (ref) || TREE_SIDE_EFFECTS (val)); |
| val = ref; |
| } |
| |
| return val; |
| } |
| |
| tree |
| mark_use (tree expr, bool rvalue_p, bool read_p, |
| location_t loc /* = UNKNOWN_LOCATION */, |
| bool reject_builtin /* = true */) |
| { |
| #define RECUR(t) mark_use ((t), rvalue_p, read_p, loc, reject_builtin) |
| |
| if (expr == NULL_TREE || error_operand_p (expr)) |
| return expr; |
| |
| if (reject_builtin) |
| return error_mark_node; |
| |
| if (read_p) |
| mark_exp_read (expr); |
| |
| bool recurse_op[3] = {false, false, false}; |
| switch (TREE_CODE (expr)) |
| { |
| case COMPONENT_REF: |
| case NON_DEPENDENT_EXPR: |
| recurse_op[0] = true; |
| break; |
| case COMPOUND_EXPR: |
| recurse_op[1] = true; |
| break; |
| case COND_EXPR: |
| recurse_op[2] = true; |
| if (TREE_OPERAND (expr, 1)) |
| recurse_op[1] = true; |
| break; |
| case INDIRECT_REF: |
| if (REFERENCE_REF_P (expr)) |
| { |
| /* Try to look through the reference. */ |
| tree ref = TREE_OPERAND (expr, 0); |
| tree r = mark_rvalue_use (ref, loc, reject_builtin); |
| if (r != ref) |
| expr = convert_from_reference (r); |
| } |
| break; |
| |
| case VIEW_CONVERT_EXPR: |
| if (location_wrapper_p (expr)) |
| { |
| loc = EXPR_LOCATION (expr); |
| tree op = TREE_OPERAND (expr, 0); |
| tree nop = RECUR (op); |
| if (nop == error_mark_node) |
| return error_mark_node; |
| else if (op == nop) |
| /* No change. */; |
| else if (DECL_P (nop) || CONSTANT_CLASS_P (nop)) |
| { |
| /* Reuse the location wrapper. */ |
| TREE_OPERAND (expr, 0) = nop; |
| /* If we're replacing a DECL with a constant, we also need to |
| change the TREE_CODE of the location wrapper. */ |
| if (rvalue_p) |
| TREE_SET_CODE (expr, NON_LVALUE_EXPR); |
| } |
| else |
| { |
| /* Drop the location wrapper. */ |
| expr = nop; |
| protected_set_expr_location (expr, loc); |
| } |
| return expr; |
| } |
| gcc_fallthrough (); |
| CASE_CONVERT: |
| recurse_op[0] = true; |
| break; |
| |
| default: |
| break; |
| } |
| |
| for (int i = 0; i < 3; ++i) |
| if (recurse_op[i]) |
| { |
| tree op = TREE_OPERAND (expr, i); |
| op = RECUR (op); |
| if (op == error_mark_node) |
| return error_mark_node; |
| TREE_OPERAND (expr, i) = op; |
| } |
| |
| return expr; |
| #undef RECUR |
| } |
| |
| tree |
| mark_rvalue_use (tree e, location_t loc /* = UNKNOWN_LOCATION */, |
| bool reject_builtin /* = true */) |
| { |
| return mark_use (e, true, true, loc, reject_builtin); |
| } |
| |
| tree |
| mark_lvalue_use (tree expr) |
| { |
| return mark_use (expr, false, true, input_location, false); |
| } |
| |
| tree |
| mark_lvalue_use_nonread (tree expr) |
| { |
| return mark_use (expr, false, false, input_location, false); |
| } |
| |
| tree |
| mark_discarded_use (tree expr) |
| { |
| if (expr == NULL_TREE) |
| return expr; |
| |
| STRIP_ANY_LOCATION_WRAPPER (expr); |
| |
| switch (TREE_CODE (expr)) |
| { |
| case COND_EXPR: |
| TREE_OPERAND (expr, 2) = mark_discarded_use (TREE_OPERAND (expr, 2)); |
| gcc_fallthrough (); |
| case COMPOUND_EXPR: |
| TREE_OPERAND (expr, 1) = mark_discarded_use (TREE_OPERAND (expr, 1)); |
| return expr; |
| |
| case COMPONENT_REF: |
| case ARRAY_REF: |
| case INDIRECT_REF: |
| case MEMBER_REF: |
| break; |
| default: |
| if (DECL_P (expr)) |
| break; |
| else |
| return expr; |
| } |
| |
| return mark_use (expr, true, true, input_location, false); |
| } |
| |
| tree |
| convert_to_void (tree expr, impl_conv_void implicit) |
| { |
| location_t loc = expr_loc_or_input_loc (expr); |
| if (expr == error_mark_node || TREE_TYPE (expr) == error_mark_node) |
| return error_mark_node; |
| |
| expr = mark_discarded_use (expr); |
| if (implicit == ICV_CAST) |
| /* An explicit cast to void avoids all -Wunused-but-set* warnings. */ |
| mark_exp_read (expr); |
| |
| if (!TREE_TYPE (expr)) |
| return expr; |
| |
| if (VOID_TYPE_P (TREE_TYPE (expr))) |
| return expr; |
| switch (TREE_CODE (expr)) |
| { |
| case COND_EXPR: { |
| /* The two parts of a cond expr might be separate lvalues. */ |
| tree op1 = TREE_OPERAND (expr, 1); |
| tree op2 = TREE_OPERAND (expr, 2); |
| bool side_effects |
| = ((op1 && TREE_SIDE_EFFECTS (op1)) || TREE_SIDE_EFFECTS (op2)); |
| tree new_op1, new_op2; |
| new_op1 = NULL_TREE; |
| if (implicit != ICV_CAST && !side_effects) |
| { |
| if (op1) |
| new_op1 = convert_to_void (op1, ICV_SECOND_OF_COND); |
| new_op2 = convert_to_void (op2, ICV_THIRD_OF_COND); |
| } |
| else |
| { |
| if (op1) |
| new_op1 = convert_to_void (op1, ICV_CAST); |
| new_op2 = convert_to_void (op2, ICV_CAST); |
| } |
| |
| expr = build3_loc (loc, COND_EXPR, TREE_TYPE (new_op2), |
| TREE_OPERAND (expr, 0), new_op1, new_op2); |
| break; |
| } |
| |
| case COMPOUND_EXPR: { |
| /* The second part of a compound expr contains the value. */ |
| tree op1 = TREE_OPERAND (expr, 1); |
| tree new_op1; |
| if (implicit != ICV_CAST |
| && !warning_suppressed_p (expr /* What warning? */)) |
| new_op1 = convert_to_void (op1, ICV_RIGHT_OF_COMMA); |
| else |
| new_op1 = convert_to_void (op1, ICV_CAST); |
| |
| if (new_op1 != op1) |
| { |
| tree t = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (new_op1), |
| TREE_OPERAND (expr, 0), new_op1); |
| expr = t; |
| } |
| |
| break; |
| } |
| |
| case NON_LVALUE_EXPR: |
| case NOP_EXPR: |
| /* These have already decayed to rvalue. */ |
| break; |
| |
| case CALL_EXPR: |
| maybe_warn_nodiscard (expr, implicit); |
| break; |
| |
| case INDIRECT_REF: { |
| tree type = TREE_TYPE (expr); |
| int is_reference = TYPE_REF_P (TREE_TYPE (TREE_OPERAND (expr, 0))); |
| int is_volatile = TYPE_VOLATILE (type); |
| int is_complete = COMPLETE_TYPE_P (type); |
| |
| /* Can't load the value if we don't know the type. */ |
| if (is_volatile && !is_complete) |
| { |
| switch (implicit) |
| { |
| case ICV_CAST: |
| warning_at (loc, 0, |
| "conversion to void will not access " |
| "object of incomplete type %qT", |
| type); |
| break; |
| case ICV_SECOND_OF_COND: |
| warning_at (loc, 0, |
| "indirection will not access object of " |
| "incomplete type %qT in second operand " |
| "of conditional expression", |
| type); |
| break; |
| case ICV_THIRD_OF_COND: |
| warning_at (loc, 0, |
| "indirection will not access object of " |
| "incomplete type %qT in third operand " |
| "of conditional expression", |
| type); |
| break; |
| case ICV_RIGHT_OF_COMMA: |
| warning_at (loc, 0, |
| "indirection will not access object of " |
| "incomplete type %qT in right operand of " |
| "comma operator", |
| type); |
| break; |
| case ICV_LEFT_OF_COMMA: |
| warning_at (loc, 0, |
| "indirection will not access object of " |
| "incomplete type %qT in left operand of " |
| "comma operator", |
| type); |
| break; |
| case ICV_STATEMENT: |
| warning_at (loc, 0, |
| "indirection will not access object of " |
| "incomplete type %qT in statement", |
| type); |
| break; |
| case ICV_THIRD_IN_FOR: |
| warning_at (loc, 0, |
| "indirection will not access object of " |
| "incomplete type %qT in for increment " |
| "expression", |
| type); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| /* Don't load the value if this is an implicit dereference, or if |
| the type needs to be handled by ctors/dtors. */ |
| else if (is_volatile && is_reference) |
| { |
| switch (implicit) |
| { |
| case ICV_CAST: |
| warning_at (loc, 0, |
| "conversion to void will not access " |
| "object of type %qT", |
| type); |
| break; |
| case ICV_SECOND_OF_COND: |
| warning_at (loc, 0, |
| "implicit dereference will not access " |
| "object of type %qT in second operand of " |
| "conditional expression", |
| type); |
| break; |
| case ICV_THIRD_OF_COND: |
| warning_at (loc, 0, |
| "implicit dereference will not access " |
| "object of type %qT in third operand of " |
| "conditional expression", |
| type); |
| break; |
| case ICV_RIGHT_OF_COMMA: |
| warning_at (loc, 0, |
| "implicit dereference will not access " |
| "object of type %qT in right operand of " |
| "comma operator", |
| type); |
| break; |
| case ICV_LEFT_OF_COMMA: |
| warning_at (loc, 0, |
| "implicit dereference will not access " |
| "object of type %qT in left operand of comma " |
| "operator", |
| type); |
| break; |
| case ICV_STATEMENT: |
| warning_at (loc, 0, |
| "implicit dereference will not access " |
| "object of type %qT in statement", |
| type); |
| break; |
| case ICV_THIRD_IN_FOR: |
| warning_at (loc, 0, |
| "implicit dereference will not access " |
| "object of type %qT in for increment expression", |
| type); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| else if (is_volatile && TREE_ADDRESSABLE (type)) |
| { |
| switch (implicit) |
| { |
| case ICV_CAST: |
| warning_at (loc, 0, |
| "conversion to void will not access " |
| "object of non-trivially-copyable type %qT", |
| type); |
| break; |
| case ICV_SECOND_OF_COND: |
| warning_at (loc, 0, |
| "indirection will not access object of " |
| "non-trivially-copyable type %qT in second " |
| "operand of conditional expression", |
| type); |
| break; |
| case ICV_THIRD_OF_COND: |
| warning_at (loc, 0, |
| "indirection will not access object of " |
| "non-trivially-copyable type %qT in third " |
| "operand of conditional expression", |
| type); |
| break; |
| case ICV_RIGHT_OF_COMMA: |
| warning_at (loc, 0, |
| "indirection will not access object of " |
| "non-trivially-copyable type %qT in right " |
| "operand of comma operator", |
| type); |
| break; |
| case ICV_LEFT_OF_COMMA: |
| warning_at (loc, 0, |
| "indirection will not access object of " |
| "non-trivially-copyable type %qT in left " |
| "operand of comma operator", |
| type); |
| break; |
| case ICV_STATEMENT: |
| warning_at (loc, 0, |
| "indirection will not access object of " |
| "non-trivially-copyable type %qT in statement", |
| type); |
| break; |
| case ICV_THIRD_IN_FOR: |
| warning_at (loc, 0, |
| "indirection will not access object of " |
| "non-trivially-copyable type %qT in for " |
| "increment expression", |
| type); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| if (is_reference || !is_volatile || !is_complete |
| || TREE_ADDRESSABLE (type)) |
| { |
| /* Emit a warning (if enabled) when the "effect-less" INDIRECT_REF |
| operation is stripped off. Note that we don't warn about |
| - an expression with TREE_NO_WARNING set. (For an example of |
| such expressions, see build_over_call in call.cc.) |
| - automatic dereferencing of references, since the user cannot |
| control it. (See also warn_if_unused_value() in c-common.cc.) |
| */ |
| if (warn_unused_value && implicit != ICV_CAST |
| && !warning_suppressed_p (expr, OPT_Wunused_value) |
| && !is_reference) |
| warning_at (loc, OPT_Wunused_value, "value computed is not used"); |
| expr = TREE_OPERAND (expr, 0); |
| if (TREE_CODE (expr) == CALL_EXPR) |
| maybe_warn_nodiscard (expr, implicit); |
| } |
| |
| break; |
| } |
| |
| case VAR_DECL: { |
| /* External variables might be incomplete. */ |
| tree type = TREE_TYPE (expr); |
| int is_complete = COMPLETE_TYPE_P (type); |
| |
| if (TYPE_VOLATILE (type) && !is_complete) |
| switch (implicit) |
| { |
| case ICV_CAST: |
| warning_at (loc, 0, |
| "conversion to void will not access " |
| "object %qE of incomplete type %qT", |
| expr, type); |
| break; |
| case ICV_SECOND_OF_COND: |
| warning_at (loc, 0, |
| "variable %qE of incomplete type %qT will " |
| "not be accessed in second operand of " |
| "conditional expression", |
| expr, type); |
| break; |
| case ICV_THIRD_OF_COND: |
| warning_at (loc, 0, |
| "variable %qE of incomplete type %qT will " |
| "not be accessed in third operand of " |
| "conditional expression", |
| expr, type); |
| break; |
| case ICV_RIGHT_OF_COMMA: |
| warning_at (loc, 0, |
| "variable %qE of incomplete type %qT will " |
| "not be accessed in right operand of comma operator", |
| expr, type); |
| break; |
| case ICV_LEFT_OF_COMMA: |
| warning_at (loc, 0, |
| "variable %qE of incomplete type %qT will " |
| "not be accessed in left operand of comma operator", |
| expr, type); |
| break; |
| case ICV_STATEMENT: |
| warning_at (loc, 0, |
| "variable %qE of incomplete type %qT will " |
| "not be accessed in statement", |
| expr, type); |
| break; |
| case ICV_THIRD_IN_FOR: |
| warning_at (loc, 0, |
| "variable %qE of incomplete type %qT will " |
| "not be accessed in for increment expression", |
| expr, type); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| break; |
| } |
| |
| default:; |
| } |
| |
| if (!TREE_SIDE_EFFECTS (expr)) |
| expr = void_node; |
| |
| return expr; |
| } |
| |
| void |
| maybe_warn_nodiscard (tree expr, impl_conv_void implicit) |
| { |
| tree call = expr; |
| if (TREE_CODE (expr) == TARGET_EXPR) |
| call = TARGET_EXPR_INITIAL (expr); |
| |
| location_t loc = expr_loc_or_input_loc (call); |
| tree callee = CALL_EXPR_FN (call); |
| if (!callee) |
| return; |
| |
| tree type = TREE_TYPE (callee); |
| if (INDIRECT_TYPE_P (type)) |
| type = TREE_TYPE (type); |
| |
| tree rettype = TREE_TYPE (type); |
| tree fn = get_fndecl_from_callee (callee); |
| tree attr; |
| if (implicit != ICV_CAST && fn |
| && (attr = lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn)))) |
| { |
| escaped_string msg; |
| tree args = TREE_VALUE (attr); |
| if (args) |
| msg.escape (TREE_STRING_POINTER (TREE_VALUE (args))); |
| const char *format |
| = (msg ? G_ ("ignoring return value of %qD, that must be used: %<%s%>") |
| : G_ ("ignoring return value of %qD, that must be used")); |
| const char *raw_msg = msg ? (const char *) msg : ""; |
| auto_diagnostic_group d; |
| if (warning_at (loc, OPT_Wunused_result, format, fn, raw_msg)) |
| inform (DECL_SOURCE_LOCATION (fn), "declared here"); |
| } |
| else if (implicit != ICV_CAST |
| && (attr |
| = lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype)))) |
| { |
| escaped_string msg; |
| tree args = TREE_VALUE (attr); |
| if (args) |
| msg.escape (TREE_STRING_POINTER (TREE_VALUE (args))); |
| const char *format |
| = (msg ? G_ ( |
| "ignoring returned value of type %qT, that must be used: %<%s%>") |
| : G_ ("ignoring returned value of type %qT, that must be used")); |
| const char *raw_msg = msg ? (const char *) msg : ""; |
| auto_diagnostic_group d; |
| if (warning_at (loc, OPT_Wunused_result, format, rettype, raw_msg)) |
| { |
| if (fn) |
| inform (DECL_SOURCE_LOCATION (fn), "in call to %qD, declared here", |
| fn); |
| inform (DECL_SOURCE_LOCATION (TYPE_NAME (rettype)), |
| "%qT declared here", rettype); |
| } |
| } |
| } |
| |
| location_t |
| expr_loc_or_loc (const_tree t, location_t or_loc) |
| { |
| location_t loc = EXPR_LOCATION (t); |
| if (loc == UNKNOWN_LOCATION) |
| loc = or_loc; |
| return loc; |
| } |
| |
| location_t |
| expr_loc_or_input_loc (const_tree t) |
| { |
| return expr_loc_or_loc (t, input_location); |
| } |
| |
| // FN is the callee of a CALL_EXPR or AGGR_INIT_EXPR; return the FUNCTION_DECL |
| // if we can. |
| tree |
| get_fndecl_from_callee (tree fn) |
| { |
| if (fn == NULL_TREE) |
| return fn; |
| if (TREE_CODE (fn) == FUNCTION_DECL) |
| return fn; |
| tree type = TREE_TYPE (fn); |
| if (type == NULL_TREE || !INDIRECT_TYPE_P (type)) |
| return NULL_TREE; |
| |
| STRIP_NOPS (fn); |
| if (TREE_CODE (fn) == ADDR_EXPR || TREE_CODE (fn) == FDESC_EXPR) |
| fn = TREE_OPERAND (fn, 0); |
| if (TREE_CODE (fn) == FUNCTION_DECL) |
| return fn; |
| return NULL_TREE; |
| } |
| |
| tree |
| pointer_offset_expression (tree base_tree, tree index_tree, location_t location) |
| { |
| tree element_type_tree = TREE_TYPE (TREE_TYPE (base_tree)); |
| if (base_tree == error_mark_node || TREE_TYPE (base_tree) == error_mark_node |
| || index_tree == error_mark_node || element_type_tree == error_mark_node) |
| return error_mark_node; |
| |
| tree element_size = TYPE_SIZE_UNIT (element_type_tree); |
| index_tree = fold_convert_loc (location, sizetype, index_tree); |
| tree offset |
| = fold_build2_loc (location, MULT_EXPR, sizetype, index_tree, element_size); |
| |
| return fold_build2_loc (location, POINTER_PLUS_EXPR, TREE_TYPE (base_tree), |
| base_tree, offset); |
| } |
| |
| // forked from gcc/cp/tree.cc cp_walk_subtrees |
| /* Apply FUNC to all language-specific sub-trees of TP in a pre-order |
| traversal. Called from walk_tree. */ |
| |
| tree |
| rs_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, void *data, |
| hash_set<tree> *pset) |
| { |
| enum tree_code code = TREE_CODE (*tp); |
| tree result; |
| |
| #define WALK_SUBTREE(NODE) \ |
| do \ |
| { \ |
| result = rs_walk_tree (&(NODE), func, data, pset); \ |
| if (result) \ |
| goto out; \ |
| } \ |
| while (0) |
| |
| if (TYPE_P (*tp)) |
| { |
| /* If *WALK_SUBTREES_P is 1, we're interested in the syntactic form of |
| the argument, so don't look through typedefs, but do walk into |
| template arguments for alias templates (and non-typedefed classes). |
| |
| If *WALK_SUBTREES_P > 1, we're interested in type identity or |
| equivalence, so look through typedefs, ignoring template arguments for |
| alias templates, and walk into template args of classes. |
| |
| See find_abi_tags_r for an example of setting *WALK_SUBTREES_P to 2 |
| when that's the behavior the walk_tree_fn wants. */ |
| if (*walk_subtrees_p == 1 && typedef_variant_p (*tp)) |
| { |
| *walk_subtrees_p = 0; |
| return NULL_TREE; |
| } |
| } |
| |
| /* Not one of the easy cases. We must explicitly go through the |
| children. */ |
| result = NULL_TREE; |
| switch (code) |
| { |
| case TREE_LIST: |
| WALK_SUBTREE (TREE_PURPOSE (*tp)); |
| break; |
| |
| case RECORD_TYPE: |
| if (TYPE_PTRMEMFUNC_P (*tp)) |
| WALK_SUBTREE (TYPE_PTRMEMFUNC_FN_TYPE_RAW (*tp)); |
| break; |
| |
| case CONSTRUCTOR: |
| if (COMPOUND_LITERAL_P (*tp)) |
| WALK_SUBTREE (TREE_TYPE (*tp)); |
| break; |
| |
| case DECL_EXPR: |
| /* User variables should be mentioned in BIND_EXPR_VARS |
| and their initializers and sizes walked when walking |
| the containing BIND_EXPR. Compiler temporaries are |
| handled here. And also normal variables in templates, |
| since do_poplevel doesn't build a BIND_EXPR then. */ |
| if (VAR_P (TREE_OPERAND (*tp, 0)) |
| && (DECL_ARTIFICIAL (TREE_OPERAND (*tp, 0)) |
| && !TREE_STATIC (TREE_OPERAND (*tp, 0)))) |
| { |
| tree decl = TREE_OPERAND (*tp, 0); |
| WALK_SUBTREE (DECL_INITIAL (decl)); |
| WALK_SUBTREE (DECL_SIZE (decl)); |
| WALK_SUBTREE (DECL_SIZE_UNIT (decl)); |
| } |
| break; |
| |
| default: |
| return NULL_TREE; |
| } |
| |
| /* We didn't find what we were looking for. */ |
| out: |
| return result; |
| |
| #undef WALK_SUBTREE |
| } |
| |
| // forked from gcc/cp/tree.cc cp_expr_location |
| |
| /* Like EXPR_LOCATION, but also handle some tcc_exceptional that have |
| locations. */ |
| |
| location_t |
| rs_expr_location (const_tree t_) |
| { |
| tree t = CONST_CAST_TREE (t_); |
| if (t == NULL_TREE) |
| return UNKNOWN_LOCATION; |
| |
| return EXPR_LOCATION (t); |
| } |
| |
| // forked from gcc/cp/class.cc is_really_empty_class |
| |
| /* Returns true if TYPE contains no actual data, just various |
| possible combinations of empty classes. If IGNORE_VPTR is true, |
| a vptr doesn't prevent the class from being considered empty. Typically |
| we want to ignore the vptr on assignment, and not on initialization. */ |
| |
| bool |
| is_really_empty_class (tree type, bool ignore_vptr) |
| { |
| if (CLASS_TYPE_P (type)) |
| { |
| tree field; |
| tree binfo; |
| tree base_binfo; |
| int i; |
| |
| /* CLASSTYPE_EMPTY_P isn't set properly until the class is actually laid |
| out, but we'd like to be able to check this before then. */ |
| if (COMPLETE_TYPE_P (type) && is_empty_class (type)) |
| return true; |
| |
| if (!ignore_vptr && TYPE_CONTAINS_VPTR_P (type)) |
| return false; |
| |
| for (binfo = TYPE_BINFO (type), i = 0; |
| BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) |
| if (!is_really_empty_class (BINFO_TYPE (base_binfo), ignore_vptr)) |
| return false; |
| for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) |
| if (TREE_CODE (field) == FIELD_DECL |
| && !DECL_ARTIFICIAL (field) |
| /* An unnamed bit-field is not a data member. */ |
| && !DECL_UNNAMED_BIT_FIELD (field) |
| && !is_really_empty_class (TREE_TYPE (field), ignore_vptr)) |
| return false; |
| return true; |
| } |
| else if (TREE_CODE (type) == ARRAY_TYPE) |
| return (integer_zerop (array_type_nelts_top (type)) |
| || is_really_empty_class (TREE_TYPE (type), ignore_vptr)); |
| return false; |
| } |
| |
| // forked from gcc/cp/class.cc is_empty_class |
| |
| /* Returns 1 if TYPE contains only padding bytes. */ |
| |
| int |
| is_empty_class (tree type) |
| { |
| if (type == error_mark_node) |
| return 0; |
| |
| if (!CLASS_TYPE_P (type)) |
| return 0; |
| |
| return CLASSTYPE_EMPTY_P (type); |
| } |
| |
| // forked from gcc/cp/tree.cc array_type_nelts_top |
| |
| /* Return, as an INTEGER_CST node, the number of elements for TYPE |
| (which is an ARRAY_TYPE). This counts only elements of the top |
| array. */ |
| |
| tree |
| array_type_nelts_top (tree type) |
| { |
| return fold_build2_loc (input_location, PLUS_EXPR, sizetype, |
| array_type_nelts (type), size_one_node); |
| } |
| |
| // forked from gcc/cp/tree.cc builtin_valid_in_constant_expr_p |
| |
| /* Test whether DECL is a builtin that may appear in a |
| constant-expression. */ |
| |
| bool |
| builtin_valid_in_constant_expr_p (const_tree decl) |
| { |
| STRIP_ANY_LOCATION_WRAPPER (decl); |
| if (TREE_CODE (decl) != FUNCTION_DECL) |
| /* Not a function. */ |
| return false; |
| if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL) |
| { |
| if (fndecl_built_in_p (decl, BUILT_IN_FRONTEND)) |
| switch (DECL_FE_FUNCTION_CODE (decl)) |
| { |
| case RS_BUILT_IN_IS_CONSTANT_EVALUATED: |
| case RS_BUILT_IN_SOURCE_LOCATION: |
| case RS_BUILT_IN_IS_CORRESPONDING_MEMBER: |
| case RS_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS: |
| return true; |
| default: |
| break; |
| } |
| /* Not a built-in. */ |
| return false; |
| } |
| switch (DECL_FUNCTION_CODE (decl)) |
| { |
| /* These always have constant results like the corresponding |
| macros/symbol. */ |
| case BUILT_IN_FILE: |
| case BUILT_IN_FUNCTION: |
| case BUILT_IN_LINE: |
| |
| /* The following built-ins are valid in constant expressions |
| when their arguments are. */ |
| case BUILT_IN_ADD_OVERFLOW_P: |
| case BUILT_IN_SUB_OVERFLOW_P: |
| case BUILT_IN_MUL_OVERFLOW_P: |
| |
| /* These have constant results even if their operands are |
| non-constant. */ |
| case BUILT_IN_CONSTANT_P: |
| case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| // forked from gcc/cp/decl2.cc decl_maybe_constant_var_p |
| |
| /* Returns true if DECL could be a symbolic constant variable, depending on |
| its initializer. */ |
| |
| bool |
| decl_maybe_constant_var_p (tree decl) |
| { |
| tree type = TREE_TYPE (decl); |
| if (!VAR_P (decl)) |
| return false; |
| if (DECL_DECLARED_CONSTEXPR_P (decl)) |
| return true; |
| if (DECL_HAS_VALUE_EXPR_P (decl)) |
| /* A proxy isn't constant. */ |
| return false; |
| if (TYPE_REF_P (type)) |
| /* References can be constant. */; |
| else if (RS_TYPE_CONST_NON_VOLATILE_P (type) |
| && INTEGRAL_OR_ENUMERATION_TYPE_P (type)) |
| /* And const integers. */; |
| else |
| return false; |
| |
| if (DECL_INITIAL (decl) && !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)) |
| /* We know the initializer, and it isn't constant. */ |
| return false; |
| else |
| return true; |
| } |
| |
| // forked from gcc/cp/typeck.cc cp_type_quals |
| |
| /* Returns the type qualifiers for this type, including the qualifiers on the |
| elements for an array type. */ |
| |
| int |
| rs_type_quals (const_tree type) |
| { |
| int quals; |
| /* This CONST_CAST is okay because strip_array_types returns its |
| argument unmodified and we assign it to a const_tree. */ |
| type = strip_array_types (CONST_CAST_TREE (type)); |
| if (type == error_mark_node |
| /* Quals on a FUNCTION_TYPE are memfn quals. */ |
| || TREE_CODE (type) == FUNCTION_TYPE) |
| return TYPE_UNQUALIFIED; |
| quals = TYPE_QUALS (type); |
| /* METHOD and REFERENCE_TYPEs should never have quals. */ |
| // gcc_assert ( |
| // (TREE_CODE (type) != METHOD_TYPE && !TYPE_REF_P (type)) |
| // || ((quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)) == |
| // TYPE_UNQUALIFIED)); |
| return quals; |
| } |
| |
| // forked from gcc/cp/decl.cc cp_global_trees |
| |
| /* The following symbols are subsumed in the cp_global_trees array, and |
| listed here individually for documentation purposes. |
| |
| C++ extensions |
| tree wchar_decl_node; |
| |
| tree vtable_entry_type; |
| tree delta_type_node; |
| tree __t_desc_type_node; |
| |
| tree class_type_node; |
| tree unknown_type_node; |
| |
| Array type `vtable_entry_type[]' |
| |
| tree vtbl_type_node; |
| tree vtbl_ptr_type_node; |
| |
| Namespaces, |
| |
| tree std_node; |
| tree abi_node; |
| |
| A FUNCTION_DECL which can call `abort'. Not necessarily the |
| one that the user will declare, but sufficient to be called |
| by routines that want to abort the program. |
| |
| tree abort_fndecl; |
| |
| Used by RTTI |
| tree type_info_type_node, tinfo_decl_id, tinfo_decl_type; |
| tree tinfo_var_id; */ |
| |
| /* The following symbols are subsumed in the c_global_trees array, and |
| listed here individually for documentation purposes. |
| |
| INTEGER_TYPE and REAL_TYPE nodes for the standard data types. |
| |
| tree short_integer_type_node; |
| tree long_integer_type_node; |
| tree long_long_integer_type_node; |
| |
| tree short_unsigned_type_node; |
| tree long_unsigned_type_node; |
| tree long_long_unsigned_type_node; |
| |
| tree truthvalue_type_node; |
| tree truthvalue_false_node; |
| tree truthvalue_true_node; |
| |
| tree ptrdiff_type_node; |
| |
| tree unsigned_char_type_node; |
| tree signed_char_type_node; |
| tree wchar_type_node; |
| |
| tree char8_type_node; |
| tree char16_type_node; |
| tree char32_type_node; |
| |
| tree float_type_node; |
| tree double_type_node; |
| tree long_double_type_node; |
| |
| tree complex_integer_type_node; |
| tree complex_float_type_node; |
| tree complex_double_type_node; |
| tree complex_long_double_type_node; |
| |
| tree dfloat32_type_node; |
| tree dfloat64_type_node; |
| tree_dfloat128_type_node; |
| |
| tree intQI_type_node; |
| tree intHI_type_node; |
| tree intSI_type_node; |
| tree intDI_type_node; |
| tree intTI_type_node; |
| |
| tree unsigned_intQI_type_node; |
| tree unsigned_intHI_type_node; |
| tree unsigned_intSI_type_node; |
| tree unsigned_intDI_type_node; |
| tree unsigned_intTI_type_node; |
| |
| tree widest_integer_literal_type_node; |
| tree widest_unsigned_literal_type_node; |
| |
| Nodes for types `void *' and `const void *'. |
| |
| tree ptr_type_node, const_ptr_type_node; |
| |
| Nodes for types `char *' and `const char *'. |
| |
| tree string_type_node, const_string_type_node; |
| |
| Type `char[SOMENUMBER]'. |
| Used when an array of char is needed and the size is irrelevant. |
| |
| tree char_array_type_node; |
| |
| Type `wchar_t[SOMENUMBER]' or something like it. |
| Used when a wide string literal is created. |
| |
| tree wchar_array_type_node; |
| |
| Type `char8_t[SOMENUMBER]' or something like it. |
| Used when a UTF-8 string literal is created. |
| |
| tree char8_array_type_node; |
| |
| Type `char16_t[SOMENUMBER]' or something like it. |
| Used when a UTF-16 string literal is created. |
| |
| tree char16_array_type_node; |
| |
| Type `char32_t[SOMENUMBER]' or something like it. |
| Used when a UTF-32 string literal is created. |
| |
| tree char32_array_type_node; |
| |
| Type `int ()' -- used for implicit declaration of functions. |
| |
| tree default_function_type; |
| |
| A VOID_TYPE node, packaged in a TREE_LIST. |
| |
| tree void_list_node; |
| |
| The lazily created VAR_DECLs for __FUNCTION__, __PRETTY_FUNCTION__, |
| and __func__. (C doesn't generate __FUNCTION__ and__PRETTY_FUNCTION__ |
| VAR_DECLS, but C++ does.) |
| |
| tree function_name_decl_node; |
| tree pretty_function_name_decl_node; |
| tree c99_function_name_decl_node; |
| |
| Stack of nested function name VAR_DECLs. |
| |
| tree saved_function_name_decls; |
| |
| */ |
| |
| // forked from gcc/cp/module.cc fixed_trees |
| |
| static GTY (()) vec<tree, va_gc> *fixed_trees; |
| |
| // forked from gcc/cp/module.cc maybe_add_global |
| |
| /* VAL is a global tree, add it to the global vec if it is |
| interesting. Add some of its targets, if they too are |
| interesting. We do not add identifiers, as they can be re-found |
| via the identifier hash table. There is a cost to the number of |
| global trees. */ |
| |
| static int |
| maybe_add_global (tree val, unsigned &crc) |
| { |
| int v = 0; |
| |
| if (val && !(TREE_CODE (val) == IDENTIFIER_NODE || TREE_VISITED (val))) |
| { |
| TREE_VISITED (val) = true; |
| crc = crc32_unsigned (crc, fixed_trees->length ()); |
| vec_safe_push (fixed_trees, val); |
| v++; |
| |
| if (CODE_CONTAINS_STRUCT (TREE_CODE (val), TS_TYPED)) |
| v += maybe_add_global (TREE_TYPE (val), crc); |
| if (CODE_CONTAINS_STRUCT (TREE_CODE (val), TS_TYPE_COMMON)) |
| v += maybe_add_global (TYPE_NAME (val), crc); |
| } |
| |
| return v; |
| } |
| |
| // forked from gcc/cp/module.cc global_tree_arys |
| |
| /* Global trees. */ |
| static const std::pair<tree *, unsigned> global_tree_arys[] = { |
| std::pair<tree *, unsigned> (cp_global_trees, CPTI_MODULE_HWM), |
| std::pair<tree *, unsigned> (c_global_trees, CTI_MODULE_HWM), |
| }; |
| |
| // forked from gcc/cp/module.cc init_modules |
| |
| void |
| init_modules () |
| { |
| unsigned crc = 0; |
| vec_alloc (fixed_trees, 200); |
| |
| const tree *ptr = global_tree_arys[0].first; |
| unsigned limit = global_tree_arys[0].second; |
| for (unsigned ix = 0; ix != limit; ix++, ptr++) |
| { |
| maybe_add_global (*ptr, crc); |
| } |
| |
| ptr = global_tree_arys[1].first; |
| limit = global_tree_arys[1].second; |
| for (unsigned ix = 0; ix != limit; ix++, ptr++) |
| { |
| maybe_add_global (*ptr, crc); |
| } |
| } |
| |
| // forked from gcc/cp/constexpr.cc var_in_constexpr_fn |
| |
| /* True if T was declared in a function declared to be constexpr, and |
| therefore potentially constant in C++14. */ |
| |
| bool |
| var_in_constexpr_fn (tree t) |
| { |
| tree ctx = DECL_CONTEXT (t); |
| return (ctx && TREE_CODE (ctx) == FUNCTION_DECL |
| && DECL_DECLARED_CONSTEXPR_P (ctx)); |
| } |
| |
| // forked from gcc/cp/name-lookup.cc member_vec_linear_search |
| |
| /* Linear search of (unordered) MEMBER_VEC for NAME. */ |
| |
| static tree |
| member_vec_linear_search (vec<tree, va_gc> *member_vec, tree name) |
| { |
| for (int ix = member_vec->length (); ix--;) |
| if (tree binding = (*member_vec)[ix]) |
| if (OVL_NAME (binding) == name) |
| return binding; |
| |
| return NULL_TREE; |
| } |
| |
| // forked from gcc/cp/name-lookup.cc member_vec_binary_search |
| |
| /* Binary search of (ordered) MEMBER_VEC for NAME. */ |
| |
| static tree |
| member_vec_binary_search (vec<tree, va_gc> *member_vec, tree name) |
| { |
| for (unsigned lo = 0, hi = member_vec->length (); lo < hi;) |
| { |
| unsigned mid = (lo + hi) / 2; |
| tree binding = (*member_vec)[mid]; |
| tree binding_name = OVL_NAME (binding); |
| |
| if (binding_name > name) |
| hi = mid; |
| else if (binding_name < name) |
| lo = mid + 1; |
| else |
| return binding; |
| } |
| |
| return NULL_TREE; |
| } |
| |
| // forked from gcc/cp/tree.cc is_overloaded_fn |
| |
| /* Returns nonzero if X is an expression for a (possibly overloaded) |
| function. If "f" is a function or function template, "f", "c->f", |
| "c.f", "C::f", and "f<int>" will all be considered possibly |
| overloaded functions. Returns 2 if the function is actually |
| overloaded, i.e., if it is impossible to know the type of the |
| function without performing overload resolution. */ |
| |
| int |
| is_overloaded_fn (tree x) |
| { |
| STRIP_ANY_LOCATION_WRAPPER (x); |
| |
| if (TREE_CODE (x) == COMPONENT_REF) |
| x = TREE_OPERAND (x, 1); |
| |
| return OVL_P (x); |
| } |
| |
| // forked from gcc/cp/tree.cc ovl_make |
| |
| /* Make a raw overload node containing FN. */ |
| |
| tree |
| ovl_make (tree fn, tree next) |
| { |
| tree result = make_node (OVERLOAD); |
| |
| if (TREE_CODE (fn) == OVERLOAD) |
| OVL_NESTED_P (result) = true; |
| |
| TREE_TYPE (result) = (next ? unknown_type_node : TREE_TYPE (fn)); |
| if (next && TREE_CODE (next) == OVERLOAD && OVL_DEDUP_P (next)) |
| OVL_DEDUP_P (result) = true; |
| OVL_FUNCTION (result) = fn; |
| OVL_CHAIN (result) = next; |
| return result; |
| } |
| |
| // forked from gcc/cp/name-lookup.cc lookup_add |
| |
| /* Add a set of new FNS into a lookup. */ |
| |
| tree |
| lookup_add (tree fns, tree lookup) |
| { |
| if (fns == error_mark_node || lookup == error_mark_node) |
| return error_mark_node; |
| |
| lookup = fns; |
| |
| return lookup; |
| } |
| |
| // forked from gcc/cp/typeck.cc type_memfn_quals |
| |
| /* Returns the function-cv-quals for TYPE, which must be a FUNCTION_TYPE or |
| METHOD_TYPE. */ |
| |
| int |
| type_memfn_quals (const_tree type) |
| { |
| if (TREE_CODE (type) == FUNCTION_TYPE) |
| return TYPE_QUALS (type); |
| else if (TREE_CODE (type) == METHOD_TYPE) |
| return rs_type_quals (class_of_this_parm (type)); |
| else |
| gcc_unreachable (); |
| } |
| |
| // forked from gcc/cp/pt.cc find_parameter_pack_data |
| |
| /* Structure used to track the progress of find_parameter_packs_r. */ |
| struct find_parameter_pack_data |
| { |
| /* TREE_LIST that will contain all of the parameter packs found by |
| the traversal. */ |
| tree *parameter_packs; |
| |
| /* Set of AST nodes that have been visited by the traversal. */ |
| hash_set<tree> *visited; |
| |
| /* True iff we're making a type pack expansion. */ |
| bool type_pack_expansion_p; |
| |
| /* True iff we found a subtree that has the extra args mechanism. */ |
| bool found_extra_args_tree_p = false; |
| }; |
| |
| // forked from gcc/cp/lex.cc conv_type_hasher |
| |
| /* Hasher for the conversion operator name hash table. */ |
| struct conv_type_hasher : ggc_ptr_hash<tree_node> |
| { |
| /* Hash NODE, an identifier node in the table. TYPE_UID is |
| suitable, as we're not concerned about matching canonicalness |
| here. */ |
| static hashval_t hash (tree node) |
| { |
| return (hashval_t) TYPE_UID (TREE_TYPE (node)); |
| } |
| |
| /* Compare NODE, an identifier node in the table, against TYPE, an |
| incoming TYPE being looked up. */ |
| static bool equal (tree node, tree type) { return TREE_TYPE (node) == type; } |
| }; |
| |
| static GTY (()) hash_table<conv_type_hasher> *conv_type_names; |
| |
| // forked from gcc/cp/lex.cc make_conv_op_name |
| |
| /* Return an identifier for a conversion operator to TYPE. We can get |
| from the returned identifier to the type. We store TYPE, which is |
| not necessarily the canonical type, which allows us to report the |
| form the user used in error messages. All these identifiers are |
| not in the identifier hash table, and have the same string name. |
| These IDENTIFIERS are not in the identifier hash table, and all |
| have the same IDENTIFIER_STRING. */ |
| |
| tree |
| make_conv_op_name (tree type) |
| { |
| if (type == error_mark_node) |
| return error_mark_node; |
| |
| if (conv_type_names == NULL) |
| conv_type_names = hash_table<conv_type_hasher>::create_ggc (31); |
| |
| tree *slot |
| = conv_type_names->find_slot_with_hash (type, (hashval_t) TYPE_UID (type), |
| INSERT); |
| tree identifier = *slot; |
| if (!identifier) |
| { |
| /* Create a raw IDENTIFIER outside of the identifier hash |
| table. */ |
| identifier = copy_node (conv_op_identifier); |
| |
| /* Just in case something managed to bind. */ |
| IDENTIFIER_BINDING (identifier) = NULL; |
| |
| /* Hang TYPE off the identifier so it can be found easily later |
| when performing conversions. */ |
| TREE_TYPE (identifier) = type; |
| |
| *slot = identifier; |
| } |
| |
| return identifier; |
| } |
| |
| // forked from gcc/cp/pt.cc builtin_pack_fn_p |
| |
| /* True iff FN is a function representing a built-in variadic parameter |
| pack. */ |
| |
| bool |
| builtin_pack_fn_p (tree fn) |
| { |
| if (!fn || TREE_CODE (fn) != FUNCTION_DECL |
| || !DECL_IS_UNDECLARED_BUILTIN (fn)) |
| return false; |
| |
| if (id_equal (DECL_NAME (fn), "__integer_pack")) |
| return true; |
| |
| return false; |
| } |
| |
| // forked from gcc/cp/pt.cc builtin_pack_call_p |
| |
| /* True iff CALL is a call to a function representing a built-in variadic |
| parameter pack. */ |
| |
| static bool |
| builtin_pack_call_p (tree call) |
| { |
| if (TREE_CODE (call) != CALL_EXPR) |
| return false; |
| return builtin_pack_fn_p (CALL_EXPR_FN (call)); |
| } |
| |
| //// forked from gcc/cp/pt.cc has_extra_args_mechanism_p |
| // |
| ///* Return true if the tree T has the extra args mechanism for |
| // avoiding partial instantiation. */ |
| // |
| // static bool |
| // has_extra_args_mechanism_p (const_tree t) |
| //{ |
| // return false; |
| //} |
| |
| // forked from gcc/cp/pt.cc find_parameter_packs_r |
| |
| /* Identifies all of the argument packs that occur in a template |
| argument and appends them to the TREE_LIST inside DATA, which is a |
| find_parameter_pack_data structure. This is a subroutine of |
| make_pack_expansion and uses_parameter_packs. */ |
| static tree |
| find_parameter_packs_r (tree *tp, int *walk_subtrees, void *data) |
| { |
| tree t = *tp; |
| struct find_parameter_pack_data *ppd |
| = (struct find_parameter_pack_data *) data; |
| bool parameter_pack_p = false; |
| |
| #define WALK_SUBTREE(NODE) \ |
| rs_walk_tree (&(NODE), &find_parameter_packs_r, ppd, ppd->visited) |
| |
| /* Don't look through typedefs; we are interested in whether a |
| parameter pack is actually written in the expression/type we're |
| looking at, not the target type. */ |
| if (TYPE_P (t) && typedef_variant_p (t)) |
| { |
| *walk_subtrees = 0; |
| return NULL_TREE; |
| } |
| |
| /* Identify whether this is a parameter pack or not. */ |
| switch (TREE_CODE (t)) |
| { |
| case FIELD_DECL: |
| case PARM_DECL: |
| break; |
| |
| case VAR_DECL: |
| break; |
| |
| case CALL_EXPR: |
| if (builtin_pack_call_p (t)) |
| parameter_pack_p = true; |
| break; |
| |
| case BASES: |
| parameter_pack_p = true; |
| break; |
| default: |
| /* Not a parameter pack. */ |
| break; |
| } |
| |
| if (parameter_pack_p) |
| { |
| /* Add this parameter pack to the list. */ |
| *ppd->parameter_packs = tree_cons (NULL_TREE, t, *ppd->parameter_packs); |
| } |
| |
| if (TYPE_P (t)) |
| rs_walk_tree (&TYPE_CONTEXT (t), &find_parameter_packs_r, ppd, |
| ppd->visited); |
| |
| /* This switch statement will return immediately if we don't find a |
| parameter pack. ??? Should some of these be in cp_walk_subtrees? */ |
| switch (TREE_CODE (t)) |
| { |
| case DECL_EXPR: { |
| tree decl = DECL_EXPR_DECL (t); |
| if (is_typedef_decl (decl)) |
| /* Since we stop at typedefs above, we need to look through them at |
| the point of the DECL_EXPR. */ |
| rs_walk_tree (&DECL_ORIGINAL_TYPE (decl), &find_parameter_packs_r, |
| ppd, ppd->visited); |
| return NULL_TREE; |
| } |
| |
| case INTEGER_TYPE: |
| rs_walk_tree (&TYPE_MAX_VALUE (t), &find_parameter_packs_r, ppd, |
| ppd->visited); |
| *walk_subtrees = 0; |
| return NULL_TREE; |
| |
| case IDENTIFIER_NODE: |
| rs_walk_tree (&TREE_TYPE (t), &find_parameter_packs_r, ppd, ppd->visited); |
| *walk_subtrees = 0; |
| return NULL_TREE; |
| |
| case DECLTYPE_TYPE: { |
| /* When traversing a DECLTYPE_TYPE_EXPR, we need to set |
| type_pack_expansion_p to false so that any placeholders |
| within the expression don't get marked as parameter packs. */ |
| bool type_pack_expansion_p = ppd->type_pack_expansion_p; |
| ppd->type_pack_expansion_p = false; |
| rs_walk_tree (&DECLTYPE_TYPE_EXPR (t), &find_parameter_packs_r, ppd, |
| ppd->visited); |
| ppd->type_pack_expansion_p = type_pack_expansion_p; |
| *walk_subtrees = 0; |
| return NULL_TREE; |
| } |
| |
| case IF_STMT: |
| rs_walk_tree (&IF_COND (t), &find_parameter_packs_r, ppd, ppd->visited); |
| rs_walk_tree (&THEN_CLAUSE (t), &find_parameter_packs_r, ppd, |
| ppd->visited); |
| rs_walk_tree (&ELSE_CLAUSE (t), &find_parameter_packs_r, ppd, |
| ppd->visited); |
| /* Don't walk into IF_STMT_EXTRA_ARGS. */ |
| *walk_subtrees = 0; |
| return NULL_TREE; |
| |
| case FUNCTION_TYPE: |
| case METHOD_TYPE: |
| WALK_SUBTREE (TYPE_RAISES_EXCEPTIONS (t)); |
| break; |
| |
| default: |
| return NULL_TREE; |
| } |
| |
| #undef WALK_SUBTREE |
| |
| return NULL_TREE; |
| } |
| |
| // forked from gcc/cp/typeck.cc type_memfn_rqual |
| |
| /* Returns the function-ref-qualifier for TYPE */ |
| |
| rs_ref_qualifier |
| type_memfn_rqual (const_tree type) |
| { |
| gcc_assert (FUNC_OR_METHOD_TYPE_P (type)); |
| |
| if (!FUNCTION_REF_QUALIFIED (type)) |
| return REF_QUAL_NONE; |
| else if (FUNCTION_RVALUE_QUALIFIED (type)) |
| return REF_QUAL_RVALUE; |
| else |
| return REF_QUAL_LVALUE; |
| } |
| |
| // forked from gcc/cp/lex.cc maybe_add_lang_type_raw |
| |
| /* Add a raw lang_type to T, a type, should it need one. */ |
| |
| bool |
| maybe_add_lang_type_raw (tree t) |
| { |
| if (!RECORD_OR_UNION_CODE_P (TREE_CODE (t))) |
| return false; |
| |
| auto *lt = (struct lang_type *) (ggc_internal_cleared_alloc ( |
| sizeof (struct lang_type))); |
| TYPE_LANG_SPECIFIC (t) = lt; |
| |
| if (GATHER_STATISTICS) |
| { |
| tree_node_counts[(int) lang_type] += 1; |
| tree_node_sizes[(int) lang_type] += sizeof (struct lang_type); |
| } |
| |
| return true; |
| } |
| |
| // forked from gcc/c-family/c-lex.cc get_fileinfo |
| |
| static splay_tree file_info_tree; |
| |
| struct c_fileinfo * |
| get_fileinfo (const char *name) |
| { |
| splay_tree_node n; |
| struct c_fileinfo *fi; |
| |
| if (!file_info_tree) |
| file_info_tree = splay_tree_new (splay_tree_compare_strings, 0, |
| splay_tree_delete_pointers); |
| |
| n = splay_tree_lookup (file_info_tree, (splay_tree_key) name); |
| if (n) |
| return (struct c_fileinfo *) n->value; |
| |
| fi = XNEW (struct c_fileinfo); |
| fi->time = 0; |
| fi->interface_only = 0; |
| fi->interface_unknown = 1; |
| splay_tree_insert (file_info_tree, (splay_tree_key) name, |
| (splay_tree_value) fi); |
| return fi; |
| } |
| |
| // forked from gcc/cp/lex.cc cxx_make_type |
| |
| tree |
| cxx_make_type (enum tree_code code MEM_STAT_DECL) |
| { |
| tree t = make_node (code PASS_MEM_STAT); |
| |
| if (maybe_add_lang_type_raw (t)) |
| { |
| /* Set up some flags that give proper default behavior. */ |
| struct c_fileinfo *finfo = get_fileinfo (LOCATION_FILE (input_location)); |
| SET_CLASSTYPE_INTERFACE_UNKNOWN_X (t, finfo->interface_unknown); |
| CLASSTYPE_INTERFACE_ONLY (t) = finfo->interface_only; |
| } |
| |
| if (code == RECORD_TYPE || code == UNION_TYPE) |
| TYPE_CXX_ODR_P (t) = 1; |
| |
| return t; |
| } |
| |
| // forked from gcc/cp/tree.cc build_min_array_type |
| |
| /* Build an ARRAY_TYPE without laying it out. */ |
| |
| static tree |
| build_min_array_type (tree elt_type, tree index_type) |
| { |
| tree t = cxx_make_type (ARRAY_TYPE); |
| TREE_TYPE (t) = elt_type; |
| TYPE_DOMAIN (t) = index_type; |
| return t; |
| } |
| |
| // forked from gcc/cp/name-lookup.cc fields_linear_search |
| |
| /* Linear search of (partially ordered) fields of KLASS for NAME. */ |
| |
| static tree |
| fields_linear_search (tree klass, tree name, bool want_type) |
| { |
| for (tree fields = TYPE_FIELDS (klass); fields; fields = DECL_CHAIN (fields)) |
| { |
| tree decl = fields; |
| |
| if (DECL_NAME (decl) != name) |
| continue; |
| |
| if (DECL_DECLARES_FUNCTION_P (decl)) |
| /* Functions are found separately. */ |
| continue; |
| |
| if (!want_type || DECL_DECLARES_TYPE_P (decl)) |
| return decl; |
| } |
| |
| return NULL_TREE; |
| } |
| |
| // forked from gcc/cp/except.cc canonnothrow_spec_pical_eh_spec |
| |
| /* Return true iff SPEC is throw() or noexcept(true). */ |
| |
| bool |
| nothrow_spec_p (const_tree spec) |
| { |
| if (spec == empty_except_spec || spec == noexcept_true_spec) |
| return true; |
| |
| gcc_assert (!spec || TREE_VALUE (spec) || spec == noexcept_false_spec |
| || TREE_PURPOSE (spec) == error_mark_node); |
| |
| return false; |
| } |
| |
| // forked from gcc/cp/tree.cc may_get_fns |
| |
| /* Get the overload set FROM refers to. Returns NULL if it's not an |
| overload set. */ |
| |
| tree |
| maybe_get_fns (tree from) |
| { |
| STRIP_ANY_LOCATION_WRAPPER (from); |
| |
| /* A baselink is also considered an overloaded function. */ |
| if (TREE_CODE (from) == COMPONENT_REF) |
| from = TREE_OPERAND (from, 1); |
| |
| if (OVL_P (from)) |
| return from; |
| |
| return NULL; |
| } |
| |
| // forked from gcc/cp/tree.cc get_fns |
| |
| /* FROM refers to an overload set. Return that set (or die). */ |
| |
| tree |
| get_fns (tree from) |
| { |
| tree res = maybe_get_fns (from); |
| |
| gcc_assert (res); |
| return res; |
| } |
| |
| // forked from gcc/cp/tree.cc get_first_fn |
| |
| /* Return the first function of the overload set FROM refers to. */ |
| |
| tree |
| get_first_fn (tree from) |
| { |
| return OVL_FIRST (get_fns (from)); |
| } |
| |
| // forked from gcc/cp/tree.cc dependent_name |
| |
| /* X is the CALL_EXPR_FN of a CALL_EXPR. If X represents a dependent name |
| (14.6.2), return the IDENTIFIER_NODE for that name. Otherwise, return |
| NULL_TREE. */ |
| |
| tree |
| dependent_name (tree x) |
| { |
| /* FIXME a dependent name must be unqualified, but this function doesn't |
| distinguish between qualified and unqualified identifiers. */ |
| if (identifier_p (x)) |
| return x; |
| |
| if (OVL_P (x)) |
| return OVL_NAME (x); |
| return NULL_TREE; |
| } |
| |
| // forked from gcc/cp/tree.cc called_fns_equal |
| |
| /* Subroutine of rs_tree_equal: t1 and t2 are the CALL_EXPR_FNs of two |
| CALL_EXPRS. Return whether they are equivalent. */ |
| |
| static bool |
| called_fns_equal (tree t1, tree t2) |
| { |
| /* Core 1321: dependent names are equivalent even if the overload sets |
| are different. But do compare explicit template arguments. */ |
| tree name1 = dependent_name (t1); |
| tree name2 = dependent_name (t2); |
| if (name1 || name2) |
| { |
| tree targs1 = NULL_TREE, targs2 = NULL_TREE; |
| |
| if (name1 != name2) |
| return false; |
| |
| /* FIXME dependent_name currently returns an unqualified name regardless |
| of whether the function was named with a qualified- or unqualified-id. |
| Until that's fixed, check that we aren't looking at overload sets from |
| different scopes. */ |
| if (is_overloaded_fn (t1) && is_overloaded_fn (t2) |
| && (DECL_CONTEXT (get_first_fn (t1)) |
| != DECL_CONTEXT (get_first_fn (t2)))) |
| return false; |
| |
| return rs_tree_equal (targs1, targs2); |
| } |
| else |
| return rs_tree_equal (t1, t2); |
| } |
| |
| // forked from gcc/cp/tree.cc canonical_eh_spec |
| |
| /* Return the canonical version of exception-specification RAISES for a C++17 |
| function type, for use in type comparison and building TYPE_CANONICAL. */ |
| |
| tree |
| canonical_eh_spec (tree raises) |
| { |
| if (raises == NULL_TREE) |
| return raises; |
| else if (nothrow_spec_p (raises)) |
| /* throw() -> noexcept. */ |
| return noexcept_true_spec; |
| else |
| /* For C++17 type matching, anything else -> nothing. */ |
| return NULL_TREE; |
| } |
| |
| /* Like cp_tree_operand_length, but takes a tree_code CODE. */ |
| |
| int |
| rs_tree_code_length (enum tree_code code) |
| { |
| gcc_assert (TREE_CODE_CLASS (code) != tcc_vl_exp); |
| |
| switch (code) |
| { |
| case PREINCREMENT_EXPR: |
| case PREDECREMENT_EXPR: |
| case POSTINCREMENT_EXPR: |
| case POSTDECREMENT_EXPR: |
| return 1; |
| |
| case ARRAY_REF: |
| return 2; |
| |
| default: |
| return TREE_CODE_LENGTH (code); |
| } |
| } |
| |
| // forked from gcc/cp/tree.cc rs_tree_operand_length |
| |
| /* Return the number of operands in T that we care about for things like |
| mangling. */ |
| |
| int |
| rs_tree_operand_length (const_tree t) |
| { |
| enum tree_code code = TREE_CODE (t); |
| |
| if (TREE_CODE_CLASS (code) == tcc_vl_exp) |
| return VL_EXP_OPERAND_LENGTH (t); |
| |
| return rs_tree_code_length (code); |
| } |
| |
| // forked from gcc/cp/tree.cc cp_tree_equal |
| |
| /* Return truthvalue of whether T1 is the same tree structure as T2. |
| Return 1 if they are the same. Return 0 if they are different. */ |
| |
| bool |
| rs_tree_equal (tree t1, tree t2) |
| { |
| enum tree_code code1, code2; |
| |
| if (t1 == t2) |
| return true; |
| if (!t1 || !t2) |
| return false; |
| |
| code1 = TREE_CODE (t1); |
| code2 = TREE_CODE (t2); |
| |
| if (code1 != code2) |
| return false; |
| |
| if (CONSTANT_CLASS_P (t1) && !same_type_p (TREE_TYPE (t1), TREE_TYPE (t2))) |
| return false; |
| |
| switch (code1) |
| { |
| case VOID_CST: |
| /* There's only a single VOID_CST node, so we should never reach |
| here. */ |
| gcc_unreachable (); |
| |
| case INTEGER_CST: |
| return tree_int_cst_equal (t1, t2); |
| |
| case REAL_CST: |
| return real_identical (&TREE_REAL_CST (t1), &TREE_REAL_CST (t2)); |
| |
| case STRING_CST: |
| return TREE_STRING_LENGTH (t1) == TREE_STRING_LENGTH (t2) |
| && !memcmp (TREE_STRING_POINTER (t1), TREE_STRING_POINTER (t2), |
| TREE_STRING_LENGTH (t1)); |
| |
| case FIXED_CST: |
| return FIXED_VALUES_IDENTICAL (TREE_FIXED_CST (t1), TREE_FIXED_CST (t2)); |
| |
| case COMPLEX_CST: |
| return rs_tree_equal (TREE_REALPART (t1), TREE_REALPART (t2)) |
| && rs_tree_equal (TREE_IMAGPART (t1), TREE_IMAGPART (t2)); |
| |
| case VECTOR_CST: |
| return operand_equal_p (t1, t2, OEP_ONLY_CONST); |
| |
| case CONSTRUCTOR: |
| /* We need to do this when determining whether or not two |
| non-type pointer to member function template arguments |
| are the same. */ |
| if (!same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)) |
| || CONSTRUCTOR_NELTS (t1) != CONSTRUCTOR_NELTS (t2)) |
| return false; |
| { |
| tree field, value; |
| unsigned int i; |
| FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (t1), i, field, value) |
| { |
| constructor_elt *elt2 = CONSTRUCTOR_ELT (t2, i); |
| if (!rs_tree_equal (field, elt2->index) |
| || !rs_tree_equal (value, elt2->value)) |
| return false; |
| } |
| } |
| return true; |
| |
| case TREE_LIST: |
| if (!rs_tree_equal (TREE_PURPOSE (t1), TREE_PURPOSE (t2))) |
| return false; |
| if (!rs_tree_equal (TREE_VALUE (t1), TREE_VALUE (t2))) |
| return false; |
| return rs_tree_equal (TREE_CHAIN (t1), TREE_CHAIN (t2)); |
| |
| case SAVE_EXPR: |
| return rs_tree_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0)); |
| |
| case CALL_EXPR: { |
| if (KOENIG_LOOKUP_P (t1) != KOENIG_LOOKUP_P (t2)) |
| return false; |
| |
| if (!called_fns_equal (CALL_EXPR_FN (t1), CALL_EXPR_FN (t2))) |
| return false; |
| |
| call_expr_arg_iterator iter1, iter2; |
| init_call_expr_arg_iterator (t1, &iter1); |
| init_call_expr_arg_iterator (t2, &iter2); |
| if (iter1.n != iter2.n) |
| return false; |
| |
| while (more_call_expr_args_p (&iter1)) |
| { |
| tree arg1 = next_call_expr_arg (&iter1); |
| tree arg2 = next_call_expr_arg (&iter2); |
| |
| gcc_checking_assert (arg1 && arg2); |
| if (!rs_tree_equal (arg1, arg2)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| case TARGET_EXPR: { |
| tree o1 = TREE_OPERAND (t1, 0); |
| tree o2 = TREE_OPERAND (t2, 0); |
| |
| /* Special case: if either target is an unallocated VAR_DECL, |
| it means that it's going to be unified with whatever the |
| TARGET_EXPR is really supposed to initialize, so treat it |
| as being equivalent to anything. */ |
| if (VAR_P (o1) && DECL_NAME (o1) == NULL_TREE && !DECL_RTL_SET_P (o1)) |
| /*Nop*/; |
| else if (VAR_P (o2) && DECL_NAME (o2) == NULL_TREE |
| && !DECL_RTL_SET_P (o2)) |
| /*Nop*/; |
| else if (!rs_tree_equal (o1, o2)) |
| return false; |
| |
| return rs_tree_equal (TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1)); |
| } |
| |
| case PARM_DECL: |
| /* For comparing uses of parameters in late-specified return types |
| with an out-of-class definition of the function, but can also come |
| up for expressions that involve 'this' in a member function |
| template. */ |
| |
| if (same_type_p (TREE_TYPE (t1), TREE_TYPE (t2))) |
| { |
| if (DECL_ARTIFICIAL (t1) ^ DECL_ARTIFICIAL (t2)) |
| return false; |
| if (CONSTRAINT_VAR_P (t1) ^ CONSTRAINT_VAR_P (t2)) |
| return false; |
| if (DECL_ARTIFICIAL (t1) |
| || (DECL_PARM_LEVEL (t1) == DECL_PARM_LEVEL (t2) |
| && DECL_PARM_INDEX (t1) == DECL_PARM_INDEX (t2))) |
| return true; |
| } |
| return false; |
| |
| case VAR_DECL: |
| case CONST_DECL: |
| case FIELD_DECL: |
| case FUNCTION_DECL: |
| case IDENTIFIER_NODE: |
| case SSA_NAME: |
| return false; |
| |
| case TREE_VEC: |
| return true; |
| |
| case NON_LVALUE_EXPR: |
| case VIEW_CONVERT_EXPR: |
| /* Used for location wrappers with possibly NULL types. */ |
| if (!TREE_TYPE (t1) || !TREE_TYPE (t2)) |
| { |
| if (TREE_TYPE (t1) || TREE_TYPE (t2)) |
| return false; |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| switch (TREE_CODE_CLASS (code1)) |
| { |
| case tcc_unary: |
| case tcc_binary: |
| case tcc_comparison: |
| case tcc_expression: |
| case tcc_vl_exp: |
| case tcc_reference: |
| case tcc_statement: { |
| int n = rs_tree_operand_length (t1); |
| if (TREE_CODE_CLASS (code1) == tcc_vl_exp |
| && n != TREE_OPERAND_LENGTH (t2)) |
| return false; |
| |
| for (int i = 0; i < n; ++i) |
| if (!rs_tree_equal (TREE_OPERAND (t1, i), TREE_OPERAND (t2, i))) |
| return false; |
| |
| return true; |
| } |
| |
| case tcc_type: |
| return same_type_p (t1, t2); |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| /* We can get here with --disable-checking. */ |
| return false; |
| } |
| |
| // forked from gcc/cp/class.cc publicly_uniquely_derived_p |
| |
| /* TRUE iff TYPE is publicly & uniquely derived from PARENT. */ |
| |
| bool publicly_uniquely_derived_p (tree, tree) { return false; } |
| |
| // forked from gcc/cp/typeck.cc comp_except_types |
| |
| /* Compare two exception specifier types for exactness or subsetness, if |
| allowed. Returns false for mismatch, true for match (same, or |
| derived and !exact). |
| |
| [except.spec] "If a class X ... objects of class X or any class publicly |
| and unambiguously derived from X. Similarly, if a pointer type Y * ... |
| exceptions of type Y * or that are pointers to any type publicly and |
| unambiguously derived from Y. Otherwise a function only allows exceptions |
| that have the same type ..." |
| This does not mention cv qualifiers and is different to what throw |
| [except.throw] and catch [except.catch] will do. They will ignore the |
| top level cv qualifiers, and allow qualifiers in the pointer to class |
| example. |
| |
| We implement the letter of the standard. */ |
| |
| static bool |
| comp_except_types (tree a, tree b, bool exact) |
| { |
| if (same_type_p (a, b)) |
| return true; |
| else if (!exact) |
| { |
| if (rs_type_quals (a) || rs_type_quals (b)) |
| return false; |
| |
| if (TYPE_PTR_P (a) && TYPE_PTR_P (b)) |
| { |
| a = TREE_TYPE (a); |
| b = TREE_TYPE (b); |
| if (rs_type_quals (a) || rs_type_quals (b)) |
| return false; |
| } |
| |
| if (TREE_CODE (a) != RECORD_TYPE || TREE_CODE (b) != RECORD_TYPE) |
| return false; |
| |
| if (publicly_uniquely_derived_p (a, b)) |
| return true; |
| } |
| return false; |
| } |
| |
| // forked from gcc/cp/typeck.cc comp_except_specs |
| |
| /* Return true if TYPE1 and TYPE2 are equivalent exception specifiers. |
| If EXACT is ce_derived, T2 can be stricter than T1 (according to 15.4/5). |
| If EXACT is ce_type, the C++17 type compatibility rules apply. |
| If EXACT is ce_normal, the compatibility rules in 15.4/3 apply. |
| If EXACT is ce_exact, the specs must be exactly the same. Exception lists |
| are unordered, but we've already filtered out duplicates. Most lists will |
| be in order, we should try to make use of that. */ |
| |
| bool |
| comp_except_specs (const_tree t1, const_tree t2, int exact) |
| { |
| const_tree probe; |
| const_tree base; |
| int length = 0; |
| |
| if (t1 == t2) |
| return true; |
| |
| /* First handle noexcept. */ |
| if (exact < ce_exact) |
| { |
| if (exact == ce_type |
| && (canonical_eh_spec (CONST_CAST_TREE (t1)) |
| == canonical_eh_spec (CONST_CAST_TREE (t2)))) |
| return true; |
| |
| /* noexcept(false) is compatible with no exception-specification, |
| and less strict than any spec. */ |
| if (t1 == noexcept_false_spec) |
| return t2 == NULL_TREE || exact == ce_derived; |
| /* Even a derived noexcept(false) is compatible with no |
| exception-specification. */ |
| if (t2 == noexcept_false_spec) |
| return t1 == NULL_TREE; |
| |
| /* Otherwise, if we aren't looking for an exact match, noexcept is |
| equivalent to throw(). */ |
| if (t1 == noexcept_true_spec) |
| t1 = empty_except_spec; |
| if (t2 == noexcept_true_spec) |
| t2 = empty_except_spec; |
| } |
| |
| /* If any noexcept is left, it is only comparable to itself; |
| either we're looking for an exact match or we're redeclaring a |
| template with dependent noexcept. */ |
| if ((t1 && TREE_PURPOSE (t1)) || (t2 && TREE_PURPOSE (t2))) |
| return (t1 && t2 && rs_tree_equal (TREE_PURPOSE (t1), TREE_PURPOSE (t2))); |
| |
| if (t1 == NULL_TREE) /* T1 is ... */ |
| return t2 == NULL_TREE || exact == ce_derived; |
| if (!TREE_VALUE (t1)) /* t1 is EMPTY */ |
| return t2 != NULL_TREE && !TREE_VALUE (t2); |
| if (t2 == NULL_TREE) /* T2 is ... */ |
| return false; |
| if (TREE_VALUE (t1) && !TREE_VALUE (t2)) /* T2 is EMPTY, T1 is not */ |
| return exact == ce_derived; |
| |
| /* Neither set is ... or EMPTY, make sure each part of T2 is in T1. |
| Count how many we find, to determine exactness. For exact matching and |
| ordered T1, T2, this is an O(n) operation, otherwise its worst case is |
| O(nm). */ |
| for (base = t1; t2 != NULL_TREE; t2 = TREE_CHAIN (t2)) |
| { |
| for (probe = base; probe != NULL_TREE; probe = TREE_CHAIN (probe)) |
| { |
| tree a = TREE_VALUE (probe); |
| tree b = TREE_VALUE (t2); |
| |
| if (comp_except_types (a, b, exact)) |
| { |
| if (probe == base && exact > ce_derived) |
| base = TREE_CHAIN (probe); |
| length++; |
| break; |
| } |
| } |
| if (probe == NULL_TREE) |
| return false; |
| } |
| return exact == ce_derived || base == NULL_TREE || length == list_length (t1); |
| } |
| |
| // forked from gcc/cp/typeck.cc compparms |
| |
| /* Subroutines of `comptypes'. */ |
| |
| /* Return true if two parameter type lists PARMS1 and PARMS2 are |
| equivalent in the sense that functions with those parameter types |
| can have equivalent types. The two lists must be equivalent, |
| element by element. */ |
| |
| bool |
| compparms (const_tree parms1, const_tree parms2) |
| { |
| const_tree t1, t2; |
| |
| /* An unspecified parmlist matches any specified parmlist |
| whose argument types don't need default promotions. */ |
| |
| for (t1 = parms1, t2 = parms2; t1 || t2; |
| t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2)) |
| { |
| /* If one parmlist is shorter than the other, |
| they fail to match. */ |
| if (!t1 || !t2) |
| return false; |
| if (!same_type_p (TREE_VALUE (t1), TREE_VALUE (t2))) |
| return false; |
| } |
| return true; |
| } |
| |
| /* Set TYPE_CANONICAL like build_array_type_1, but using |
| build_cplus_array_type. */ |
| |
| static void |
| set_array_type_canon (tree t, tree elt_type, tree index_type, bool dep) |
| { |
| /* Set the canonical type for this new node. */ |
| if (TYPE_STRUCTURAL_EQUALITY_P (elt_type) |
| || (index_type && TYPE_STRUCTURAL_EQUALITY_P (index_type))) |
| SET_TYPE_STRUCTURAL_EQUALITY (t); |
| else if (TYPE_CANONICAL (elt_type) != elt_type |
| || (index_type && TYPE_CANONICAL (index_type) != index_type)) |
| TYPE_CANONICAL (t) |
| = build_cplus_array_type (TYPE_CANONICAL (elt_type), |
| index_type ? TYPE_CANONICAL (index_type) |
| : index_type, |
| dep); |
| else |
| TYPE_CANONICAL (t) = t; |
| } |
| |
| // forked from gcc/cp/tree.cc cplus_array_info |
| |
| struct cplus_array_info |
| { |
| tree type; |
| tree domain; |
| }; |
| |
| // forked from gcc/cp/tree.cc cplus_array_hasher |
| |
| struct cplus_array_hasher : ggc_ptr_hash<tree_node> |
| { |
| typedef cplus_array_info *compare_type; |
| |
| static hashval_t hash (tree t); |
| static bool equal (tree, cplus_array_info *); |
| }; |
| |
| /* Hash an ARRAY_TYPE. K is really of type `tree'. */ |
| |
| hashval_t |
| cplus_array_hasher::hash (tree t) |
| { |
| hashval_t hash; |
| |
| hash = TYPE_UID (TREE_TYPE (t)); |
| if (TYPE_DOMAIN (t)) |
| hash ^= TYPE_UID (TYPE_DOMAIN (t)); |
| return hash; |
| } |
| |
| /* Compare two ARRAY_TYPEs. K1 is really of type `tree', K2 is really |
| of type `cplus_array_info*'. */ |
| |
| bool |
| cplus_array_hasher::equal (tree t1, cplus_array_info *t2) |
| { |
| return (TREE_TYPE (t1) == t2->type && TYPE_DOMAIN (t1) == t2->domain); |
| } |
| |
| // forked from gcc/cp/tree.cc cplus_array_htab |
| |
| /* Hash table containing dependent array types, which are unsuitable for |
| the language-independent type hash table. */ |
| static GTY (()) hash_table<cplus_array_hasher> *cplus_array_htab; |
| |
| // forked from gcc/cp/tree.cc is_byte_access_type |
| |
| /* Returns true if TYPE is char, unsigned char, or std::byte. */ |
| |
| bool |
| is_byte_access_type (tree type) |
| { |
| type = TYPE_MAIN_VARIANT (type); |
| if (type == char_type_node || type == unsigned_char_type_node) |
| return true; |
| |
| return (TREE_CODE (type) == ENUMERAL_TYPE && TYPE_CONTEXT (type) == std_node |
| && !strcmp ("byte", TYPE_NAME_STRING (type))); |
| } |
| |
| // forked from gcc/cp/tree.cc build_cplus_array_type |
| |
| /* Like build_array_type, but handle special C++ semantics: an array of a |
| variant element type is a variant of the array of the main variant of |
| the element type. IS_DEPENDENT is -ve if we should determine the |
| dependency. Otherwise its bool value indicates dependency. */ |
| |
| tree |
| build_cplus_array_type (tree elt_type, tree index_type, int dependent) |
| { |
| tree t; |
| |
| if (elt_type == error_mark_node || index_type == error_mark_node) |
| return error_mark_node; |
| |
| if (dependent < 0) |
| dependent = 0; |
| |
| if (elt_type != TYPE_MAIN_VARIANT (elt_type)) |
| /* Start with an array of the TYPE_MAIN_VARIANT. */ |
| t = build_cplus_array_type (TYPE_MAIN_VARIANT (elt_type), index_type, |
| dependent); |
| else if (dependent) |
| { |
| /* Since type_hash_canon calls layout_type, we need to use our own |
| hash table. */ |
| cplus_array_info cai; |
| hashval_t hash; |
| |
| if (cplus_array_htab == NULL) |
| cplus_array_htab = hash_table<cplus_array_hasher>::create_ggc (61); |
| |
| hash = TYPE_UID (elt_type); |
| if (index_type) |
| hash ^= TYPE_UID (index_type); |
| cai.type = elt_type; |
| cai.domain = index_type; |
| |
| tree *e = cplus_array_htab->find_slot_with_hash (&cai, hash, INSERT); |
| if (*e) |
| /* We have found the type: we're done. */ |
| return (tree) *e; |
| else |
| { |
| /* Build a new array type. */ |
| t = build_min_array_type (elt_type, index_type); |
| |
| /* Store it in the hash table. */ |
| *e = t; |
| |
| /* Set the canonical type for this new node. */ |
| set_array_type_canon (t, elt_type, index_type, dependent); |
| |
| /* Mark it as dependent now, this saves time later. */ |
| TYPE_DEPENDENT_P_VALID (t) = true; |
| TYPE_DEPENDENT_P (t) = true; |
| } |
| } |
| else |
| { |
| bool typeless_storage = is_byte_access_type (elt_type); |
| t = build_array_type (elt_type, index_type, typeless_storage); |
| |
| /* Mark as non-dependenty now, this will save time later. */ |
| TYPE_DEPENDENT_P_VALID (t) = true; |
| } |
| |
| /* Now check whether we already have this array variant. */ |
| if (elt_type != TYPE_MAIN_VARIANT (elt_type)) |
| { |
| tree m = t; |
| for (t = m; t; t = TYPE_NEXT_VARIANT (t)) |
| if (TREE_TYPE (t) == elt_type && TYPE_NAME (t) == NULL_TREE |
| && TYPE_ATTRIBUTES (t) == NULL_TREE) |
| break; |
| if (!t) |
| { |
| t = build_min_array_type (elt_type, index_type); |
| /* Mark dependency now, this saves time later. */ |
| TYPE_DEPENDENT_P_VALID (t) = true; |
| TYPE_DEPENDENT_P (t) = dependent; |
| set_array_type_canon (t, elt_type, index_type, dependent); |
| if (!dependent) |
| { |
| layout_type (t); |
| /* Make sure sizes are shared with the main variant. |
| layout_type can't be called after setting TYPE_NEXT_VARIANT, |
| as it will overwrite alignment etc. of all variants. */ |
| TYPE_SIZE (t) = TYPE_SIZE (m); |
| TYPE_SIZE_UNIT (t) = TYPE_SIZE_UNIT (m); |
| TYPE_TYPELESS_STORAGE (t) = TYPE_TYPELESS_STORAGE (m); |
| } |
| |
| TYPE_MAIN_VARIANT (t) = m; |
| TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (m); |
| TYPE_NEXT_VARIANT (m) = t; |
| } |
| } |
| |
| /* Avoid spurious warnings with VLAs (c++/54583). */ |
| if (TYPE_SIZE (t) && EXPR_P (TYPE_SIZE (t))) |
| suppress_warning (TYPE_SIZE (t), OPT_Wunused); |
| |
| /* Push these needs up to the ARRAY_TYPE so that initialization takes |
| place more easily. */ |
| bool needs_ctor |
| = (TYPE_NEEDS_CONSTRUCTING (t) = TYPE_NEEDS_CONSTRUCTING (elt_type)); |
| bool needs_dtor = (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) |
| = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (elt_type)); |
| |
| if (!dependent && t == TYPE_MAIN_VARIANT (t) && !COMPLETE_TYPE_P (t) |
| && COMPLETE_TYPE_P (elt_type)) |
| { |
| /* The element type has been completed since the last time we saw |
| this array type; update the layout and 'tor flags for any variants |
| that need it. */ |
| layout_type (t); |
| for (tree v = TYPE_NEXT_VARIANT (t); v; v = TYPE_NEXT_VARIANT (v)) |
| { |
| TYPE_NEEDS_CONSTRUCTING (v) = needs_ctor; |
| TYPE_HAS_NONTRIVIAL_DESTRUCTOR (v) = needs_dtor; |
| } |
| } |
| |
| return t; |
| } |
| |
| // forked from gcc/cp/tree.cc cp_build_qualified_type_real |
| |
| /* Make a variant of TYPE, qualified with the TYPE_QUALS. Handles |
| arrays correctly. In particular, if TYPE is an array of T's, and |
| TYPE_QUALS is non-empty, returns an array of qualified T's. |
| |
| FLAGS determines how to deal with ill-formed qualifications. If |
| tf_ignore_bad_quals is set, then bad qualifications are dropped |
| (this is permitted if TYPE was introduced via a typedef or template |
| type parameter). If bad qualifications are dropped and tf_warning |
| is set, then a warning is issued for non-const qualifications. If |
| tf_ignore_bad_quals is not set and tf_error is not set, we |
| return error_mark_node. Otherwise, we issue an error, and ignore |
| the qualifications. |
| |
| Qualification of a reference type is valid when the reference came |
| via a typedef or template type argument. [dcl.ref] No such |
| dispensation is provided for qualifying a function type. [dcl.fct] |
| DR 295 queries this and the proposed resolution brings it into line |
| with qualifying a reference. We implement the DR. We also behave |
| in a similar manner for restricting non-pointer types. */ |
| |
| tree |
| rs_build_qualified_type_real (tree type, int type_quals, |
| tsubst_flags_t complain) |
| { |
| tree result; |
| int bad_quals = TYPE_UNQUALIFIED; |
| |
| if (type == error_mark_node) |
| return type; |
| |
| if (type_quals == rs_type_quals (type)) |
| return type; |
| |
| if (TREE_CODE (type) == ARRAY_TYPE) |
| { |
| /* In C++, the qualification really applies to the array element |
| type. Obtain the appropriately qualified element type. */ |
| tree t; |
| tree element_type |
| = rs_build_qualified_type_real (TREE_TYPE (type), type_quals, complain); |
| |
| if (element_type == error_mark_node) |
| return error_mark_node; |
| |
| /* See if we already have an identically qualified type. Tests |
| should be equivalent to those in check_qualified_type. */ |
| for (t = TYPE_MAIN_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t)) |
| if (TREE_TYPE (t) == element_type && TYPE_NAME (t) == TYPE_NAME (type) |
| && TYPE_CONTEXT (t) == TYPE_CONTEXT (type) |
| && attribute_list_equal (TYPE_ATTRIBUTES (t), |
| TYPE_ATTRIBUTES (type))) |
| break; |
| |
| if (!t) |
| { |
| /* If we already know the dependentness, tell the array type |
| constructor. This is important for module streaming, as we cannot |
| dynamically determine that on read in. */ |
| t = build_cplus_array_type (element_type, TYPE_DOMAIN (type), |
| TYPE_DEPENDENT_P_VALID (type) |
| ? int (TYPE_DEPENDENT_P (type)) |
| : -1); |
| |
| /* Keep the typedef name. */ |
| if (TYPE_NAME (t) != TYPE_NAME (type)) |
| { |
| t = build_variant_type_copy (t); |
| TYPE_NAME (t) = TYPE_NAME (type); |
| SET_TYPE_ALIGN (t, TYPE_ALIGN (type)); |
| TYPE_USER_ALIGN (t) = TYPE_USER_ALIGN (type); |
| } |
| } |
| |
| /* Even if we already had this variant, we update |
| TYPE_NEEDS_CONSTRUCTING and TYPE_HAS_NONTRIVIAL_DESTRUCTOR in case |
| they changed since the variant was originally created. |
| |
| This seems hokey; if there is some way to use a previous |
| variant *without* coming through here, |
| TYPE_NEEDS_CONSTRUCTING will never be updated. */ |
| TYPE_NEEDS_CONSTRUCTING (t) |
| = TYPE_NEEDS_CONSTRUCTING (TYPE_MAIN_VARIANT (element_type)); |
| TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) |
| = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TYPE_MAIN_VARIANT (element_type)); |
| return t; |
| } |
| |
| /* A reference or method type shall not be cv-qualified. |
| [dcl.ref], [dcl.fct]. This used to be an error, but as of DR 295 |
| (in CD1) we always ignore extra cv-quals on functions. */ |
| |
| /* [dcl.ref/1] Cv-qualified references are ill-formed except when |
| the cv-qualifiers are introduced through the use of a typedef-name |
| ([dcl.typedef], [temp.param]) or decltype-specifier |
| ([dcl.type.decltype]),in which case the cv-qualifiers are |
| ignored. */ |
| if (type_quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE) |
| && (TYPE_REF_P (type) || FUNC_OR_METHOD_TYPE_P (type))) |
| { |
| if (TYPE_REF_P (type) |
| && (!typedef_variant_p (type) || FUNC_OR_METHOD_TYPE_P (type))) |
| bad_quals |= type_quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE); |
| type_quals &= ~(TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE); |
| } |
| |
| /* But preserve any function-cv-quals on a FUNCTION_TYPE. */ |
| if (TREE_CODE (type) == FUNCTION_TYPE) |
| type_quals |= type_memfn_quals (type); |
| |
| /* A restrict-qualified type must be a pointer (or reference) |
| to object or incomplete type. */ |
| if ((type_quals & TYPE_QUAL_RESTRICT) && TREE_CODE (type) != TYPENAME_TYPE |
| && !INDIRECT_TYPE_P (type)) |
| { |
| bad_quals |= TYPE_QUAL_RESTRICT; |
| type_quals &= ~TYPE_QUAL_RESTRICT; |
| } |
| |
| if (bad_quals == TYPE_UNQUALIFIED || (complain & tf_ignore_bad_quals)) |
| /*OK*/; |
| else if (!(complain & tf_error)) |
| return error_mark_node; |
| else |
| { |
| tree bad_type = build_qualified_type (ptr_type_node, bad_quals); |
| error ("%qV qualifiers cannot be applied to %qT", bad_type, type); |
| } |
| |
| /* Retrieve (or create) the appropriately qualified variant. */ |
| result = build_qualified_type (type, type_quals); |
| |
| return result; |
| } |
| |
| // forked from gcc/cp/c-common.cc vector_targets_convertible_p |
| |
| /* vector_targets_convertible_p is used for vector pointer types. The |
| callers perform various checks that the qualifiers are satisfactory, |
| while OTOH vector_targets_convertible_p ignores the number of elements |
| in the vectors. That's fine with vector pointers as we can consider, |
| say, a vector of 8 elements as two consecutive vectors of 4 elements, |
| and that does not require and conversion of the pointer values. |
| In contrast, vector_types_convertible_p and |
| vector_types_compatible_elements_p are used for vector value types. */ |
| /* True if pointers to distinct types T1 and T2 can be converted to |
| each other without an explicit cast. Only returns true for opaque |
| vector types. */ |
| bool |
| vector_targets_convertible_p (const_tree t1, const_tree t2) |
| { |
| if (VECTOR_TYPE_P (t1) && VECTOR_TYPE_P (t2) |
| && (TYPE_VECTOR_OPAQUE (t1) || TYPE_VECTOR_OPAQUE (t2)) |
| && tree_int_cst_equal (TYPE_SIZE (t1), TYPE_SIZE (t2))) |
| return true; |
| |
| return false; |
| } |
| |
| // forked from gcc/cp/typeck.cc comp_array_types |
| |
| /* Compare the array types T1 and T2. CB says how we should behave when |
| comparing array bounds: bounds_none doesn't allow dimensionless arrays, |
| bounds_either says than any array can be [], bounds_first means that |
| onlt T1 can be an array with unknown bounds. STRICT is true if |
| qualifiers must match when comparing the types of the array elements. */ |
| |
| static bool |
| comp_array_types (const_tree t1, const_tree t2, compare_bounds_t cb, |
| bool strict) |
| { |
| tree d1; |
| tree d2; |
| tree max1, max2; |
| |
| if (t1 == t2) |
| return true; |
| |
| /* The type of the array elements must be the same. */ |
| if (strict ? !same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)) |
| : !similar_type_p (TREE_TYPE (t1), TREE_TYPE (t2))) |
| return false; |
| |
| d1 = TYPE_DOMAIN (t1); |
| d2 = TYPE_DOMAIN (t2); |
| |
| if (d1 == d2) |
| return true; |
| |
| /* If one of the arrays is dimensionless, and the other has a |
| dimension, they are of different types. However, it is valid to |
| write: |
| |
| extern int a[]; |
| int a[3]; |
| |
| by [basic.link]: |
| |
| declarations for an array object can specify |
| array types that differ by the presence or absence of a major |
| array bound (_dcl.array_). */ |
| if (!d1 && d2) |
| return cb >= bounds_either; |
| else if (d1 && !d2) |
| return cb == bounds_either; |
| |
| /* Check that the dimensions are the same. */ |
| |
| if (!rs_tree_equal (TYPE_MIN_VALUE (d1), TYPE_MIN_VALUE (d2))) |
| return false; |
| max1 = TYPE_MAX_VALUE (d1); |
| max2 = TYPE_MAX_VALUE (d2); |
| |
| if (!rs_tree_equal (max1, max2)) |
| return false; |
| |
| return true; |
| } |
| |
| // forked from gcc/cp/typeck.cc same_type_ignoring_top_level_qualifiers_p |
| |
| /* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring |
| top-level qualifiers. */ |
| |
| bool |
| same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2) |
| { |
| if (type1 == error_mark_node || type2 == error_mark_node) |
| return false; |
| if (type1 == type2) |
| return true; |
| |
| type1 = rs_build_qualified_type (type1, TYPE_UNQUALIFIED); |
| type2 = rs_build_qualified_type (type2, TYPE_UNQUALIFIED); |
| return same_type_p (type1, type2); |
| } |
| |
| // forked from gcc/cp/typeck.cc comp_ptr_ttypes_const |
| |
| /* Return true if TO and FROM (both of which are POINTER_TYPEs or |
| pointer-to-member types) are the same, ignoring cv-qualification at |
| all levels. CB says how we should behave when comparing array bounds. */ |
| |
| bool |
| comp_ptr_ttypes_const (tree to, tree from, compare_bounds_t cb) |
| { |
| bool is_opaque_pointer = false; |
| |
| for (;; to = TREE_TYPE (to), from = TREE_TYPE (from)) |
| { |
| if (TREE_CODE (to) != TREE_CODE (from)) |
| return false; |
| |
| if (TREE_CODE (from) == OFFSET_TYPE |
| && same_type_p (TYPE_OFFSET_BASETYPE (from), |
| TYPE_OFFSET_BASETYPE (to))) |
| continue; |
| |
| if (VECTOR_TYPE_P (to)) |
| is_opaque_pointer = vector_targets_convertible_p (to, from); |
| |
| if (TREE_CODE (to) == ARRAY_TYPE |
| /* Ignore cv-qualification, but if we see e.g. int[3] and int[4], |
| we must fail. */ |
| && !comp_array_types (to, from, cb, /*strict=*/false)) |
| return false; |
| |
| /* CWG 330 says we need to look through arrays. */ |
| if (!TYPE_PTR_P (to) && TREE_CODE (to) != ARRAY_TYPE) |
| return (is_opaque_pointer |
| || same_type_ignoring_top_level_qualifiers_p (to, from)); |
| } |
| } |
| |
| // forked from gcc/cp/typeck.cc similar_type_p |
| |
| /* Returns nonzero iff TYPE1 and TYPE2 are similar, as per [conv.qual]. */ |
| |
| bool |
| similar_type_p (tree type1, tree type2) |
| { |
| if (type1 == error_mark_node || type2 == error_mark_node) |
| return false; |
| |
| /* Informally, two types are similar if, ignoring top-level cv-qualification: |
| * they are the same type; or |
| * they are both pointers, and the pointed-to types are similar; or |
| * they are both pointers to member of the same class, and the types of |
| the pointed-to members are similar; or |
| * they are both arrays of the same size or both arrays of unknown bound, |
| and the array element types are similar. */ |
| |
| if (same_type_ignoring_top_level_qualifiers_p (type1, type2)) |
| return true; |
| |
| if ((TYPE_PTR_P (type1) && TYPE_PTR_P (type2)) |
| || (TYPE_PTRDATAMEM_P (type1) && TYPE_PTRDATAMEM_P (type2)) |
| || (TREE_CODE (type1) == ARRAY_TYPE && TREE_CODE (type2) == ARRAY_TYPE)) |
| return comp_ptr_ttypes_const (type1, type2, bounds_either); |
| |
| return false; |
| } |
| |
| // forked from gcc/cp/typeck.cc structural_comptypes |
| // note: this fork only handles strict == COMPARE_STRICT |
| // if you pass in any other value for strict i.e. COMPARE_BASE, |
| // COMPARE_DERIVED, COMPARE_REDECLARATION or COMPARE_STRUCTURAL |
| // see the original function in gcc/cp/typeck.cc and port the required bits |
| // specifically under case UNION_TYPE. |
| |
| /* Subroutine in comptypes. */ |
| |
| static bool |
| structural_comptypes (tree t1, tree t2, int strict) |
| { |
| /* Both should be types that are not obviously the same. */ |
| gcc_checking_assert (t1 != t2 && TYPE_P (t1) && TYPE_P (t2)); |
| |
| if (TYPE_PTRMEMFUNC_P (t1)) |
| t1 = TYPE_PTRMEMFUNC_FN_TYPE (t1); |
| if (TYPE_PTRMEMFUNC_P (t2)) |
| t2 = TYPE_PTRMEMFUNC_FN_TYPE (t2); |
| |
| /* Different classes of types can't be compatible. */ |
| if (TREE_CODE (t1) != TREE_CODE (t2)) |
| return false; |
| |
| /* Qualifiers must match. For array types, we will check when we |
| recur on the array element types. */ |
| if (TREE_CODE (t1) != ARRAY_TYPE && rs_type_quals (t1) != rs_type_quals (t2)) |
| return false; |
| if (TREE_CODE (t1) == FUNCTION_TYPE |
| && type_memfn_quals (t1) != type_memfn_quals (t2)) |
| return false; |
| /* Need to check this before TYPE_MAIN_VARIANT. |
| FIXME function qualifiers should really change the main variant. */ |
| if (FUNC_OR_METHOD_TYPE_P (t1)) |
| { |
| if (type_memfn_rqual (t1) != type_memfn_rqual (t2)) |
| return false; |
| if (/* cxx_dialect >= cxx17 && */ |
| !comp_except_specs (TYPE_RAISES_EXCEPTIONS (t1), |
| TYPE_RAISES_EXCEPTIONS (t2), ce_type)) |
| return false; |
| } |
| |
| /* Allow for two different type nodes which have essentially the same |
| definition. Note that we already checked for equality of the type |
| qualifiers (just above). */ |
| if (TREE_CODE (t1) != ARRAY_TYPE |
| && TYPE_MAIN_VARIANT (t1) == TYPE_MAIN_VARIANT (t2)) |
| return true; |
| |
| /* Compare the types. Return false on known not-same. Break on not |
| known. Never return true from this switch -- you'll break |
| specialization comparison. */ |
| switch (TREE_CODE (t1)) |
| { |
| case VOID_TYPE: |
| case BOOLEAN_TYPE: |
| /* All void and bool types are the same. */ |
| break; |
| |
| case OPAQUE_TYPE: |
| case INTEGER_TYPE: |
| case FIXED_POINT_TYPE: |
| case REAL_TYPE: |
| /* With these nodes, we can't determine type equivalence by |
| looking at what is stored in the nodes themselves, because |
| two nodes might have different TYPE_MAIN_VARIANTs but still |
| represent the same type. For example, wchar_t and int could |
| have the same properties (TYPE_PRECISION, TYPE_MIN_VALUE, |
| TYPE_MAX_VALUE, etc.), but have different TYPE_MAIN_VARIANTs |
| and are distinct types. On the other hand, int and the |
| following typedef |
| |
| typedef int INT __attribute((may_alias)); |
| |
| have identical properties, different TYPE_MAIN_VARIANTs, but |
| represent the same type. The canonical type system keeps |
| track of equivalence in this case, so we fall back on it. */ |
| if (TYPE_CANONICAL (t1) != TYPE_CANONICAL (t2)) |
| return false; |
| |
| /* We don't need or want the attribute comparison. */ |
| return true; |
| |
| case RECORD_TYPE: |
| case UNION_TYPE: |
| return false; |
| |
| case OFFSET_TYPE: |
| if (!comptypes (TYPE_OFFSET_BASETYPE (t1), TYPE_OFFSET_BASETYPE (t2), |
| strict & ~COMPARE_REDECLARATION)) |
| return false; |
| if (!same_type_p (TREE_TYPE (t1), TREE_TYPE (t2))) |
| return false; |
| break; |
| |
| case REFERENCE_TYPE: |
| if (TYPE_REF_IS_RVALUE (t1) != TYPE_REF_IS_RVALUE (t2)) |
| return false; |
| /* fall through to checks for pointer types */ |
| gcc_fallthrough (); |
| |
| case POINTER_TYPE: |
| if (TYPE_MODE (t1) != TYPE_MODE (t2) |
| || !same_type_p (TREE_TYPE (t1), TREE_TYPE (t2))) |
| return false; |
| break; |
| |
| case METHOD_TYPE: |
| case FUNCTION_TYPE: |
| /* Exception specs and memfn_rquals were checked above. */ |
| if (!same_type_p (TREE_TYPE (t1), TREE_TYPE (t2))) |
| return false; |
| if (!compparms (TYPE_ARG_TYPES (t1), TYPE_ARG_TYPES (t2))) |
| return false; |
| break; |
| |
| case ARRAY_TYPE: |
| /* Target types must match incl. qualifiers. */ |
| if (!comp_array_types (t1, t2, |
| ((strict & COMPARE_REDECLARATION) ? bounds_either |
| : bounds_none), |
| /*strict=*/true)) |
| return false; |
| break; |
| |
| case COMPLEX_TYPE: |
| if (!same_type_p (TREE_TYPE (t1), TREE_TYPE (t2))) |
| return false; |
| break; |
| |
| case VECTOR_TYPE: |
| if (gnu_vector_type_p (t1) != gnu_vector_type_p (t2) |
| || maybe_ne (TYPE_VECTOR_SUBPARTS (t1), TYPE_VECTOR_SUBPARTS (t2)) |
| || !same_type_p (TREE_TYPE (t1), TREE_TYPE (t2))) |
| return false; |
| break; |
| |
| default: |
| return false; |
| } |
| |
| /* If we get here, we know that from a target independent POV the |
| types are the same. Make sure the target attributes are also |
| the same. */ |
| if (!comp_type_attributes (t1, t2)) |
| return false; |
| |
| return true; |
| } |
| |
| // forked from gcc/cp/typeck.cc comptypes |
| |
| /* Return true if T1 and T2 are related as allowed by STRICT. STRICT |
| is a bitwise-or of the COMPARE_* flags. */ |
| |
| bool |
| comptypes (tree t1, tree t2, int strict) |
| { |
| gcc_checking_assert (t1 && t2); |
| |
| /* TYPE_ARGUMENT_PACKS are not really types. */ |
| gcc_checking_assert (TREE_CODE (t1) != TYPE_ARGUMENT_PACK |
| && TREE_CODE (t2) != TYPE_ARGUMENT_PACK); |
| |
| if (t1 == t2) |
| return true; |
| |
| /* Suppress errors caused by previously reported errors. */ |
| if (t1 == error_mark_node || t2 == error_mark_node) |
| return false; |
| |
| if (strict == COMPARE_STRICT) |
| { |
| if (TYPE_STRUCTURAL_EQUALITY_P (t1) || TYPE_STRUCTURAL_EQUALITY_P (t2)) |
| /* At least one of the types requires structural equality, so |
| perform a deep check. */ |
| return structural_comptypes (t1, t2, strict); |
| |
| if (!flag_checking) |
| return TYPE_CANONICAL (t1) == TYPE_CANONICAL (t2); |
| else |
| return structural_comptypes (t1, t2, strict); |
| } |
| else if (strict == COMPARE_STRUCTURAL) |
| return structural_comptypes (t1, t2, COMPARE_STRICT); |
| else |
| return structural_comptypes (t1, t2, strict); |
| } |
| |
| // forked from gcc/cp/decl.cc next_initializable_field |
| |
| /* FIELD is an element of TYPE_FIELDS or NULL. In the former case, the value |
| returned is the next FIELD_DECL (possibly FIELD itself) that can be |
| initialized. If there are no more such fields, the return value |
| will be NULL. */ |
| |
| tree |
| next_initializable_field (tree field) |
| { |
| while (field |
| && (TREE_CODE (field) != FIELD_DECL || DECL_UNNAMED_BIT_FIELD (field) |
| || (DECL_ARTIFICIAL (field) |
| /* Don't skip vptr fields. We might see them when we're |
| called from reduced_constant_expression_p. */ |
| && !DECL_VIRTUAL_P (field)))) |
| field = DECL_CHAIN (field); |
| |
| return field; |
| } |
| |
| // forked from gcc/cp/call.cc sufficient_parms_p |
| |
| /* Returns nonzero if PARMLIST consists of only default parms, |
| ellipsis, and/or undeduced parameter packs. */ |
| |
| bool |
| sufficient_parms_p (const_tree parmlist) |
| { |
| for (; parmlist && parmlist != void_list_node; |
| parmlist = TREE_CHAIN (parmlist)) |
| if (!TREE_PURPOSE (parmlist)) |
| return false; |
| return true; |
| } |
| |
| // forked from gcc/cp/class.cc default_ctor_p |
| |
| /* Returns true if FN is a default constructor. */ |
| |
| bool |
| default_ctor_p (const_tree fn) |
| { |
| return (DECL_CONSTRUCTOR_P (fn) |
| && sufficient_parms_p (FUNCTION_FIRST_USER_PARMTYPE (fn))); |
| } |
| |
| // forked from gcc/cp/class.cc user_provided_p |
| |
| /* Returns true iff FN is a user-provided function, i.e. user-declared |
| and not defaulted at its first declaration. */ |
| |
| bool |
| user_provided_p (tree fn) |
| { |
| return (!DECL_ARTIFICIAL (fn) |
| && !(DECL_INITIALIZED_IN_CLASS_P (fn) |
| && (DECL_DEFAULTED_FN (fn) || DECL_DELETED_FN (fn)))); |
| } |
| |
| // forked from gcc/cp/class.cc type_has_non_user_provided_default_constructor |
| |
| /* Returns true iff class T has a non-user-provided (i.e. implicitly |
| declared or explicitly defaulted in the class body) default |
| constructor. */ |
| |
| bool |
| type_has_non_user_provided_default_constructor (tree t) |
| { |
| if (!TYPE_HAS_DEFAULT_CONSTRUCTOR (t)) |
| return false; |
| if (CLASSTYPE_LAZY_DEFAULT_CTOR (t)) |
| return true; |
| |
| for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (t)); iter; ++iter) |
| { |
| tree fn = *iter; |
| if (TREE_CODE (fn) == FUNCTION_DECL && default_ctor_p (fn) |
| && !user_provided_p (fn)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // forked from gcc/cp/class.cc default_init_uninitialized_part |
| |
| /* If default-initialization leaves part of TYPE uninitialized, returns |
| a DECL for the field or TYPE itself (DR 253). */ |
| |
| tree |
| default_init_uninitialized_part (tree type) |
| { |
| tree t, r, binfo; |
| int i; |
| |
| type = strip_array_types (type); |
| if (!CLASS_TYPE_P (type)) |
| return type; |
| if (!type_has_non_user_provided_default_constructor (type)) |
| return NULL_TREE; |
| for (binfo = TYPE_BINFO (type), i = 0; BINFO_BASE_ITERATE (binfo, i, t); ++i) |
| { |
| r = default_init_uninitialized_part (BINFO_TYPE (t)); |
| if (r) |
| return r; |
| } |
| for (t = next_initializable_field (TYPE_FIELDS (type)); t; |
| t = next_initializable_field (DECL_CHAIN (t))) |
| if (!DECL_INITIAL (t) && !DECL_ARTIFICIAL (t)) |
| { |
| r = default_init_uninitialized_part (TREE_TYPE (t)); |
| if (r) |
| return DECL_P (r) ? r : t; |
| } |
| |
| return NULL_TREE; |
| } |
| |
| // forked from gcc/cp/name-lookup.cc extract_conversion_operator |
| |
| /* FNS is an overload set of conversion functions. Return the |
| overloads converting to TYPE. */ |
| |
| static tree |
| extract_conversion_operator (tree fns, tree type) |
| { |
| tree convs = NULL_TREE; |
| tree tpls = NULL_TREE; |
| |
| for (ovl_iterator iter (fns); iter; ++iter) |
| { |
| if (same_type_p (DECL_CONV_FN_TYPE (*iter), type)) |
| convs = lookup_add (*iter, convs); |
| } |
| |
| if (!convs) |
| convs = tpls; |
| |
| return convs; |
| } |
| |
| // forked from gcc/cp/name-lookup.cc |
| |
| /* Look for NAME as an immediate member of KLASS (including |
| anon-members or unscoped enum member). TYPE_OR_FNS is zero for |
| regular search. >0 to get a type binding (if there is one) and <0 |
| if you want (just) the member function binding. |
| |
| Use this if you do not want lazy member creation. */ |
| |
| tree |
| get_class_binding_direct (tree klass, tree name, bool want_type) |
| { |
| gcc_checking_assert (RECORD_OR_UNION_TYPE_P (klass)); |
| |
| /* Conversion operators can only be found by the marker conversion |
| operator name. */ |
| bool conv_op = IDENTIFIER_CONV_OP_P (name); |
| tree lookup = conv_op ? conv_op_identifier : name; |
| tree val = NULL_TREE; |
| vec<tree, va_gc> *member_vec = CLASSTYPE_MEMBER_VEC (klass); |
| |
| if (COMPLETE_TYPE_P (klass) && member_vec) |
| { |
| val = member_vec_binary_search (member_vec, lookup); |
| if (!val) |
| ; |
| else if (STAT_HACK_P (val)) |
| val = want_type ? STAT_TYPE (val) : STAT_DECL (val); |
| else if (want_type && !DECL_DECLARES_TYPE_P (val)) |
| val = NULL_TREE; |
| } |
| else |
| { |
| if (member_vec && !want_type) |
| val = member_vec_linear_search (member_vec, lookup); |
| |
| if (!val || (TREE_CODE (val) == OVERLOAD && OVL_DEDUP_P (val))) |
| /* Dependent using declarations are a 'field', make sure we |
| return that even if we saw an overload already. */ |
| if (tree field_val = fields_linear_search (klass, lookup, want_type)) |
| { |
| if (!val) |
| val = field_val; |
| else if (TREE_CODE (field_val) == USING_DECL) |
| val = ovl_make (field_val, val); |
| } |
| } |
| |
| /* Extract the conversion operators asked for, unless the general |
| conversion operator was requested. */ |
| if (val && conv_op) |
| { |
| gcc_checking_assert (OVL_FUNCTION (val) == conv_op_marker); |
| val = OVL_CHAIN (val); |
| if (tree type = TREE_TYPE (name)) |
| val = extract_conversion_operator (val, type); |
| } |
| |
| return val; |
| } |
| |
| #if defined ENABLE_TREE_CHECKING |
| |
| // forked from gcc/cp/tree.cc lang_check_failed |
| |
| /* Complain that some language-specific thing hanging off a tree |
| node has been accessed improperly. */ |
| |
| void |
| lang_check_failed (const char *file, int line, const char *function) |
| { |
| internal_error ("%<lang_*%> check: failed in %s, at %s:%d", function, |
| trim_filename (file), line); |
| } |
| #endif /* ENABLE_TREE_CHECKING */ |
| |
| // forked from gcc/cp/tree.cc skip_artificial_parms_for |
| |
| /* Given a FUNCTION_DECL FN and a chain LIST, skip as many elements of LIST |
| as there are artificial parms in FN. */ |
| |
| tree |
| skip_artificial_parms_for (const_tree fn, tree list) |
| { |
| if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)) |
| list = TREE_CHAIN (list); |
| else |
| return list; |
| |
| if (DECL_HAS_IN_CHARGE_PARM_P (fn)) |
| list = TREE_CHAIN (list); |
| if (DECL_HAS_VTT_PARM_P (fn)) |
| list = TREE_CHAIN (list); |
| return list; |
| } |
| |
| // forked from gcc/cp/class.cc in_class_defaulted_default_constructor |
| |
| /* Returns the defaulted constructor if T has one. Otherwise, returns |
| NULL_TREE. */ |
| |
| tree |
| in_class_defaulted_default_constructor (tree t) |
| { |
| if (!TYPE_HAS_USER_CONSTRUCTOR (t)) |
| return NULL_TREE; |
| |
| for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (t)); iter; ++iter) |
| { |
| tree fn = *iter; |
| |
| if (DECL_DEFAULTED_IN_CLASS_P (fn) && default_ctor_p (fn)) |
| return fn; |
| } |
| |
| return NULL_TREE; |
| } |
| |
| // forked from gcc/cp/constexpr.cc |
| |
| /* Returns true iff FUN is an instantiation of a constexpr function |
| template or a defaulted constexpr function. */ |
| |
| bool |
| is_instantiation_of_constexpr (tree fun) |
| { |
| return ((DECL_DEFAULTED_FN (fun) && DECL_DECLARED_CONSTEXPR_P (fun))); |
| } |
| |
| // forked from gcc/cp/decl.cc check_for_uninitialized_const_var |
| |
| /* Issue an error message if DECL is an uninitialized const variable. |
| CONSTEXPR_CONTEXT_P is true when the function is called in a constexpr |
| context from potential_constant_expression. Returns true if all is well, |
| false otherwise. */ |
| |
| bool |
| check_for_uninitialized_const_var (tree decl, bool constexpr_context_p, |
| tsubst_flags_t complain) |
| { |
| tree type = strip_array_types (TREE_TYPE (decl)); |
| |
| /* ``Unless explicitly declared extern, a const object does not have |
| external linkage and must be initialized. ($8.4; $12.1)'' ARM |
| 7.1.6 */ |
| if (VAR_P (decl) && !TYPE_REF_P (type) && (RS_TYPE_CONST_P (type)) |
| && !DECL_NONTRIVIALLY_INITIALIZED_P (decl)) |
| { |
| tree field = default_init_uninitialized_part (type); |
| if (!field) |
| return true; |
| |
| bool show_notes = true; |
| |
| if (!constexpr_context_p) |
| { |
| if (RS_TYPE_CONST_P (type)) |
| { |
| if (complain & tf_error) |
| show_notes = permerror (DECL_SOURCE_LOCATION (decl), |
| "uninitialized %<const %D%>", decl); |
| } |
| else |
| { |
| if (!is_instantiation_of_constexpr (current_function_decl) |
| && (complain & tf_error)) |
| error_at (DECL_SOURCE_LOCATION (decl), |
| "uninitialized variable %qD in %<constexpr%> " |
| "function", |
| decl); |
| else |
| show_notes = false; |
| } |
| } |
| else if (complain & tf_error) |
| error_at (DECL_SOURCE_LOCATION (decl), |
| "uninitialized variable %qD in %<constexpr%> context", decl); |
| |
| if (show_notes && CLASS_TYPE_P (type) && (complain & tf_error)) |
| { |
| // tree defaulted_ctor; |
| |
| // inform (DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (type)), |
| // "%q#T has no user-provided default constructor", type); |
| // defaulted_ctor = in_class_defaulted_default_constructor (type); |
| // if (defaulted_ctor) |
| // inform (DECL_SOURCE_LOCATION (defaulted_ctor), |
| // "constructor is not user-provided because it is " |
| // "explicitly defaulted in the class body"); |
| // inform (DECL_SOURCE_LOCATION (field), |
| // "and the implicitly-defined constructor does not " |
| // "initialize %q#D", |
| // field); |
| } |
| |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // forked from gcc/cp/tree.cc cv_unqualified |
| |
| /* Return TYPE with const and volatile removed. */ |
| |
| tree |
| cv_unqualified (tree type) |
| { |
| int quals; |
| |
| if (type == error_mark_node) |
| return type; |
| |
| quals = rs_type_quals (type); |
| quals &= ~(TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE); |
| return rs_build_qualified_type (type, quals); |
| } |
| |
| /* The C and C++ parsers both use vectors to hold function arguments. |
| For efficiency, we keep a cache of unused vectors. This is the |
| cache. */ |
| |
| typedef vec<tree, va_gc> *tree_gc_vec; |
| static GTY ((deletable)) vec<tree_gc_vec, va_gc> *tree_vector_cache; |
| |
| // forked from gcc/c-family/c-common.c make_tree_vector |
| |
| /* Return a new vector from the cache. If the cache is empty, |
| allocate a new vector. These vectors are GC'ed, so it is OK if the |
| pointer is not released.. */ |
| |
| vec<tree, va_gc> * |
| make_tree_vector (void) |
| { |
| if (tree_vector_cache && !tree_vector_cache->is_empty ()) |
| return tree_vector_cache->pop (); |
| else |
| { |
| /* Passing 0 to vec::alloc returns NULL, and our callers require |
| that we always return a non-NULL value. The vector code uses |
| 4 when growing a NULL vector, so we do too. */ |
| vec<tree, va_gc> *v; |
| vec_alloc (v, 4); |
| return v; |
| } |
| } |
| |
| // forked from gcc/c-family/c-common.c release_tree_vector |
| |
| /* Release a vector of trees back to the cache. */ |
| |
| void |
| release_tree_vector (vec<tree, va_gc> *vec) |
| { |
| if (vec != NULL) |
| { |
| if (vec->allocated () >= 16) |
| /* Don't cache vecs that have expanded more than once. On a p64 |
| target, vecs double in alloc size with each power of 2 elements, e.g |
| at 16 elements the alloc increases from 128 to 256 bytes. */ |
| vec_free (vec); |
| else |
| { |
| vec->truncate (0); |
| vec_safe_push (tree_vector_cache, vec); |
| } |
| } |
| } |
| |
| // forked from gcc/cp/cvt.cc instantiation_dependent_expression_p |
| |
| /* As above, but also check value-dependence of the expression as a whole. */ |
| |
| bool instantiation_dependent_expression_p (tree) { return false; } |
| |
| // forked from gcc/cp/cvt.cc cp_get_callee |
| |
| /* If CALL is a call, return the callee; otherwise null. */ |
| |
| tree |
| cp_get_callee (tree call) |
| { |
| if (call == NULL_TREE) |
| return call; |
| else if (TREE_CODE (call) == CALL_EXPR) |
| return CALL_EXPR_FN (call); |
| return NULL_TREE; |
| } |
| |
| // forked from gcc/cp/typeck.cc build_nop |
| |
| /* Return a NOP_EXPR converting EXPR to TYPE. */ |
| |
| tree |
| build_nop (tree type, tree expr) |
| { |
| if (type == error_mark_node || error_operand_p (expr)) |
| return expr; |
| return build1_loc (EXPR_LOCATION (expr), NOP_EXPR, type, expr); |
| } |
| |
| // forked from gcc/cp/tree.cc scalarish_type_p |
| |
| /* Returns 1 iff type T is something we want to treat as a scalar type for |
| the purpose of deciding whether it is trivial/POD/standard-layout. */ |
| |
| bool |
| scalarish_type_p (const_tree t) |
| { |
| if (t == error_mark_node) |
| return 1; |
| |
| return (SCALAR_TYPE_P (t) || VECTOR_TYPE_P (t)); |
| } |
| |
| // forked from gcc/cp/tree.cc type_has_nontrivial_copy_init |
| |
| /* Returns true iff copying an object of type T (including via move |
| constructor) is non-trivial. That is, T has no non-trivial copy |
| constructors and no non-trivial move constructors, and not all copy/move |
| constructors are deleted. This function implements the ABI notion of |
| non-trivial copy, which has diverged from the one in the standard. */ |
| |
| bool type_has_nontrivial_copy_init (const_tree) { return false; } |
| |
| // forked from gcc/cp/tree.cc build_local_temp |
| |
| /* Return an undeclared local temporary of type TYPE for use in building a |
| TARGET_EXPR. */ |
| |
| tree |
| build_local_temp (tree type) |
| { |
| tree slot = build_decl (input_location, VAR_DECL, NULL_TREE, type); |
| DECL_ARTIFICIAL (slot) = 1; |
| DECL_IGNORED_P (slot) = 1; |
| DECL_CONTEXT (slot) = current_function_decl; |
| layout_decl (slot, 0); |
| return slot; |
| } |
| |
| // forked from gcc/cp/lambda.cc is_normal_capture_proxy |
| |
| /* Returns true iff DECL is a capture proxy for a normal capture |
| (i.e. without explicit initializer). */ |
| |
| bool is_normal_capture_proxy (tree) { return false; } |
| |
| // forked from gcc/cp/c-common.cc reject_gcc_builtin |
| |
| /* For an EXPR of a FUNCTION_TYPE that references a GCC built-in function |
| with no library fallback or for an ADDR_EXPR whose operand is such type |
| issues an error pointing to the location LOC. |
| Returns true when the expression has been diagnosed and false |
| otherwise. */ |
| |
| bool |
| reject_gcc_builtin (const_tree expr, location_t loc /* = UNKNOWN_LOCATION */) |
| { |
| if (TREE_CODE (expr) == ADDR_EXPR) |
| expr = TREE_OPERAND (expr, 0); |
| |
| STRIP_ANY_LOCATION_WRAPPER (expr); |
| |
| if (TREE_TYPE (expr) && TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE |
| && TREE_CODE (expr) == FUNCTION_DECL |
| /* The intersection of DECL_BUILT_IN and DECL_IS_UNDECLARED_BUILTIN avoids |
| false positives for user-declared built-ins such as abs or |
| strlen, and for C++ operators new and delete. |
| The c_decl_implicit() test avoids false positives for implicitly |
| declared built-ins with library fallbacks (such as abs). */ |
| && fndecl_built_in_p (expr) && DECL_IS_UNDECLARED_BUILTIN (expr) |
| && !DECL_ASSEMBLER_NAME_SET_P (expr)) |
| { |
| if (loc == UNKNOWN_LOCATION) |
| loc = EXPR_LOC_OR_LOC (expr, input_location); |
| |
| /* Reject arguments that are built-in functions with |
| no library fallback. */ |
| error_at (loc, "built-in function %qE must be directly called", expr); |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // forked from gcc/cp/typeck.cc is_bitfield_expr_with_lowered_type |
| |
| /* If EXP is a reference to a bit-field, and the type of EXP does not |
| match the declared type of the bit-field, return the declared type |
| of the bit-field. Otherwise, return NULL_TREE. */ |
| |
| tree |
| is_bitfield_expr_with_lowered_type (const_tree exp) |
| { |
| switch (TREE_CODE (exp)) |
| { |
| case COND_EXPR: |
| if (!is_bitfield_expr_with_lowered_type (TREE_OPERAND (exp, 1) |
| ? TREE_OPERAND (exp, 1) |
| : TREE_OPERAND (exp, 0))) |
| return NULL_TREE; |
| return is_bitfield_expr_with_lowered_type (TREE_OPERAND (exp, 2)); |
| |
| case COMPOUND_EXPR: |
| return is_bitfield_expr_with_lowered_type (TREE_OPERAND (exp, 1)); |
| |
| case MODIFY_EXPR: |
| case SAVE_EXPR: |
| case UNARY_PLUS_EXPR: |
| case PREDECREMENT_EXPR: |
| case PREINCREMENT_EXPR: |
| case POSTDECREMENT_EXPR: |
| case POSTINCREMENT_EXPR: |
| case NEGATE_EXPR: |
| case NON_LVALUE_EXPR: |
| case BIT_NOT_EXPR: |
| return is_bitfield_expr_with_lowered_type (TREE_OPERAND (exp, 0)); |
| |
| case COMPONENT_REF: { |
| tree field; |
| |
| field = TREE_OPERAND (exp, 1); |
| if (TREE_CODE (field) != FIELD_DECL || !DECL_BIT_FIELD_TYPE (field)) |
| return NULL_TREE; |
| if (same_type_ignoring_top_level_qualifiers_p ( |
| TREE_TYPE (exp), DECL_BIT_FIELD_TYPE (field))) |
| return NULL_TREE; |
| return DECL_BIT_FIELD_TYPE (field); |
| } |
| |
| case VAR_DECL: |
| if (DECL_HAS_VALUE_EXPR_P (exp)) |
| return is_bitfield_expr_with_lowered_type ( |
| DECL_VALUE_EXPR (CONST_CAST_TREE (exp))); |
| return NULL_TREE; |
| |
| case VIEW_CONVERT_EXPR: |
| if (location_wrapper_p (exp)) |
| return is_bitfield_expr_with_lowered_type (TREE_OPERAND (exp, 0)); |
| else |
| return NULL_TREE; |
| |
| default: |
| return NULL_TREE; |
| } |
| } |
| |
| // forked from gcc/cp/semantics.cc maybe_undo_parenthesized_ref |
| |
| /* If T is an id-expression obfuscated by force_paren_expr, undo the |
| obfuscation and return the underlying id-expression. Otherwise |
| return T. */ |
| |
| tree |
| maybe_undo_parenthesized_ref (tree t) |
| { |
| if ((TREE_CODE (t) == PAREN_EXPR || TREE_CODE (t) == VIEW_CONVERT_EXPR) |
| && REF_PARENTHESIZED_P (t)) |
| t = TREE_OPERAND (t, 0); |
| |
| return t; |
| } |
| |
| // forked from gcc/c-family/c-common.cc fold_offsetof |
| |
| /* Fold an offsetof-like expression. EXPR is a nested sequence of component |
| references with an INDIRECT_REF of a constant at the bottom; much like the |
| traditional rendering of offsetof as a macro. TYPE is the desired type of |
| the whole expression. Return the folded result. */ |
| |
| tree |
| fold_offsetof (tree expr, tree type, enum tree_code ctx) |
| { |
| tree base, off, t; |
| tree_code code = TREE_CODE (expr); |
| switch (code) |
| { |
| case ERROR_MARK: |
| return expr; |
| |
| case VAR_DECL: |
| error ("cannot apply %<offsetof%> to static data member %qD", expr); |
| return error_mark_node; |
| |
| case CALL_EXPR: |
| case TARGET_EXPR: |
| error ("cannot apply %<offsetof%> when %<operator[]%> is overloaded"); |
| return error_mark_node; |
| |
| case NOP_EXPR: |
| case INDIRECT_REF: |
| if (!TREE_CONSTANT (TREE_OPERAND (expr, 0))) |
| { |
| error ("cannot apply %<offsetof%> to a non constant address"); |
| return error_mark_node; |
| } |
| return convert (type, TREE_OPERAND (expr, 0)); |
| |
| case COMPONENT_REF: |
| base = fold_offsetof (TREE_OPERAND (expr, 0), type, code); |
| if (base == error_mark_node) |
| return base; |
| |
| t = TREE_OPERAND (expr, 1); |
| if (DECL_C_BIT_FIELD (t)) |
| { |
| error ("attempt to take address of bit-field structure " |
| "member %qD", |
| t); |
| return error_mark_node; |
| } |
| off = size_binop_loc (input_location, PLUS_EXPR, DECL_FIELD_OFFSET (t), |
| size_int (tree_to_uhwi (DECL_FIELD_BIT_OFFSET (t)) |
| / BITS_PER_UNIT)); |
| break; |
| |
| case ARRAY_REF: |
| base = fold_offsetof (TREE_OPERAND (expr, 0), type, code); |
| if (base == error_mark_node) |
| return base; |
| |
| t = TREE_OPERAND (expr, 1); |
| STRIP_ANY_LOCATION_WRAPPER (t); |
| |
| /* Check if the offset goes beyond the upper bound of the array. */ |
| if (TREE_CODE (t) == INTEGER_CST && tree_int_cst_sgn (t) >= 0) |
| { |
| tree upbound = array_ref_up_bound (expr); |
| if (upbound != NULL_TREE && TREE_CODE (upbound) == INTEGER_CST |
| && !tree_int_cst_equal (upbound, |
| TYPE_MAX_VALUE (TREE_TYPE (upbound)))) |
| { |
| if (ctx != ARRAY_REF && ctx != COMPONENT_REF) |
| upbound = size_binop (PLUS_EXPR, upbound, |
| build_int_cst (TREE_TYPE (upbound), 1)); |
| if (tree_int_cst_lt (upbound, t)) |
| { |
| tree v; |
| |
| for (v = TREE_OPERAND (expr, 0); |
| TREE_CODE (v) == COMPONENT_REF; v = TREE_OPERAND (v, 0)) |
| if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) |
| == RECORD_TYPE) |
| { |
| tree fld_chain = DECL_CHAIN (TREE_OPERAND (v, 1)); |
| for (; fld_chain; fld_chain = DECL_CHAIN (fld_chain)) |
| if (TREE_CODE (fld_chain) == FIELD_DECL) |
| break; |
| |
| if (fld_chain) |
| break; |
| } |
| /* Don't warn if the array might be considered a poor |
| man's flexible array member with a very permissive |
| definition thereof. */ |
| if (TREE_CODE (v) == ARRAY_REF |
| || TREE_CODE (v) == COMPONENT_REF) |
| warning (OPT_Warray_bounds_, |
| "index %E denotes an offset " |
| "greater than size of %qT", |
| t, TREE_TYPE (TREE_OPERAND (expr, 0))); |
| } |
| } |
| } |
| |
| t = convert (sizetype, t); |
| off = size_binop (MULT_EXPR, TYPE_SIZE_UNIT (TREE_TYPE (expr)), t); |
| break; |
| |
| case COMPOUND_EXPR: |
| /* Handle static members of volatile structs. */ |
| t = TREE_OPERAND (expr, 1); |
| gcc_checking_assert (VAR_P (get_base_address (t))); |
| return fold_offsetof (t, type); |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (!POINTER_TYPE_P (type)) |
| return size_binop (PLUS_EXPR, base, convert (type, off)); |
| return fold_build_pointer_plus (base, off); |
| } |
| |
| // forked from gcc/cp/tree.cc char_type_p |
| |
| /* Returns nonzero if TYPE is a character type, including wchar_t. */ |
| |
| int |
| char_type_p (tree type) |
| { |
| return (same_type_p (type, char_type_node) |
| || same_type_p (type, unsigned_char_type_node) |
| || same_type_p (type, signed_char_type_node) |
| || same_type_p (type, char8_type_node) |
| || same_type_p (type, char16_type_node) |
| || same_type_p (type, char32_type_node) |
| || same_type_p (type, wchar_type_node)); |
| } |
| |
| // forked from gcc/cp/pt.cc resolve_nondeduced_context |
| |
| /* Core DR 115: In contexts where deduction is done and fails, or in |
| contexts where deduction is not done, if a template argument list is |
| specified and it, along with any default template arguments, identifies |
| a single function template specialization, then the template-id is an |
| lvalue for the function template specialization. */ |
| |
| tree |
| resolve_nondeduced_context (tree orig_expr, tsubst_flags_t) |
| { |
| return orig_expr; |
| } |
| |
| // forked from gcc/cp/pt.cc instantiate_non_dependent_or_null |
| |
| /* Like instantiate_non_dependent_expr, but return NULL_TREE rather than |
| an uninstantiated expression. */ |
| |
| tree |
| instantiate_non_dependent_or_null (tree expr) |
| { |
| if (expr == NULL_TREE) |
| return NULL_TREE; |
| |
| return expr; |
| } |
| |
| // forked from gcc/cp/pt.cc resolve_nondeduced_context_or_error |
| |
| /* As above, but error out if the expression remains overloaded. */ |
| |
| tree |
| resolve_nondeduced_context_or_error (tree exp, tsubst_flags_t complain) |
| { |
| exp = resolve_nondeduced_context (exp, complain); |
| if (type_unknown_p (exp)) |
| { |
| if (complain & tf_error) |
| cxx_incomplete_type_error (exp, TREE_TYPE (exp)); |
| return error_mark_node; |
| } |
| return exp; |
| } |
| |
| // forked from gcc/cp/tree.cc really_overloaded_fn |
| |
| /* Returns true iff X is an expression for an overloaded function |
| whose type cannot be known without performing overload |
| resolution. */ |
| |
| bool |
| really_overloaded_fn (tree x) |
| { |
| return is_overloaded_fn (x) == 2; |
| } |
| |
| // forked from gcc/cp/typeck..cc invalid_nonstatic_memfn_p |
| |
| /* EXPR is being used in a context that is not a function call. |
| Enforce: |
| |
| [expr.ref] |
| |
| The expression can be used only as the left-hand operand of a |
| member function call. |
| |
| [expr.mptr.operator] |
| |
| If the result of .* or ->* is a function, then that result can be |
| used only as the operand for the function call operator (). |
| |
| by issuing an error message if appropriate. Returns true iff EXPR |
| violates these rules. */ |
| |
| bool |
| invalid_nonstatic_memfn_p (location_t loc, tree expr, tsubst_flags_t complain) |
| { |
| if (expr == NULL_TREE) |
| return false; |
| /* Don't enforce this in MS mode. */ |
| if (flag_ms_extensions) |
| return false; |
| if (is_overloaded_fn (expr) && !really_overloaded_fn (expr)) |
| expr = get_first_fn (expr); |
| if (DECL_NONSTATIC_MEMBER_FUNCTION_P (expr)) |
| { |
| if (complain & tf_error) |
| { |
| if (DECL_P (expr)) |
| { |
| error_at (loc, "invalid use of non-static member function %qD", |
| expr); |
| inform (DECL_SOURCE_LOCATION (expr), "declared here"); |
| } |
| else |
| error_at (loc, |
| "invalid use of non-static member function of " |
| "type %qT", |
| TREE_TYPE (expr)); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| // forked from gcc/cp/call.cc strip_top_quals |
| |
| tree |
| strip_top_quals (tree t) |
| { |
| if (TREE_CODE (t) == ARRAY_TYPE) |
| return t; |
| return rs_build_qualified_type (t, 0); |
| } |
| |
| // forked from gcc/cp/typeck2.cc cxx_incomplete_type_inform |
| |
| /* Print an inform about the declaration of the incomplete type TYPE. */ |
| |
| // void |
| // cxx_incomplete_type_inform (const_tree type) |
| // { |
| // if (!TYPE_MAIN_DECL (type)) |
| // return; |
| |
| // location_t loc = DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (type)); |
| // tree ptype = strip_top_quals (CONST_CAST_TREE (type)); |
| |
| // if (current_class_type && TYPE_BEING_DEFINED (current_class_type) |
| // && same_type_p (ptype, current_class_type)) |
| // inform (loc, |
| // "definition of %q#T is not complete until " |
| // "the closing brace", |
| // ptype); |
| // else |
| // inform (loc, "forward declaration of %q#T", ptype); |
| // } |
| |
| // forked from gcc/cp/typeck2.cc cxx_incomplete_type_diagnostic |
| |
| /* Print an error message for invalid use of an incomplete type. |
| VALUE is the expression that was used (or 0 if that isn't known) |
| and TYPE is the type that was invalid. DIAG_KIND indicates the |
| type of diagnostic (see diagnostic.def). */ |
| |
| void |
| cxx_incomplete_type_diagnostic (location_t loc, const_tree value, |
| const_tree type, diagnostic_t diag_kind) |
| { |
| // bool is_decl = false, complained = false; |
| |
| gcc_assert (diag_kind == DK_WARNING || diag_kind == DK_PEDWARN |
| || diag_kind == DK_ERROR); |
| |
| /* Avoid duplicate error message. */ |
| if (TREE_CODE (type) == ERROR_MARK) |
| return; |
| |
| if (value) |
| { |
| STRIP_ANY_LOCATION_WRAPPER (value); |
| |
| if (VAR_P (value) || TREE_CODE (value) == PARM_DECL |
| || TREE_CODE (value) == FIELD_DECL) |
| { |
| // complained = emit_diagnostic (diag_kind, DECL_SOURCE_LOCATION |
| // (value), |
| // 0, "%qD has incomplete type", value); |
| // is_decl = true; |
| } |
| } |
| retry: |
| /* We must print an error message. Be clever about what it says. */ |
| |
| switch (TREE_CODE (type)) |
| { |
| // case RECORD_TYPE: |
| // case UNION_TYPE: |
| // case ENUMERAL_TYPE: |
| // if (!is_decl) |
| // complained |
| // = emit_diagnostic (diag_kind, loc, 0, |
| // "invalid use of incomplete type %q#T", type); |
| // if (complained) |
| // cxx_incomplete_type_inform (type); |
| // break; |
| |
| case VOID_TYPE: |
| emit_diagnostic (diag_kind, loc, 0, "invalid use of %qT", type); |
| break; |
| |
| case ARRAY_TYPE: |
| if (TYPE_DOMAIN (type)) |
| { |
| type = TREE_TYPE (type); |
| goto retry; |
| } |
| emit_diagnostic (diag_kind, loc, 0, |
| "invalid use of array with unspecified bounds"); |
| break; |
| |
| case OFFSET_TYPE: |
| bad_member : { |
| tree member = TREE_OPERAND (value, 1); |
| if (is_overloaded_fn (member)) |
| member = get_first_fn (member); |
| |
| if (DECL_FUNCTION_MEMBER_P (member) && !flag_ms_extensions) |
| { |
| gcc_rich_location richloc (loc); |
| /* If "member" has no arguments (other than "this"), then |
| add a fix-it hint. */ |
| if (type_num_arguments (TREE_TYPE (member)) == 1) |
| richloc.add_fixit_insert_after ("()"); |
| emit_diagnostic (diag_kind, &richloc, 0, |
| "invalid use of member function %qD " |
| "(did you forget the %<()%> ?)", |
| member); |
| } |
| else |
| emit_diagnostic (diag_kind, loc, 0, |
| "invalid use of member %qD " |
| "(did you forget the %<&%> ?)", |
| member); |
| } |
| break; |
| |
| case LANG_TYPE: |
| if (type == init_list_type_node) |
| { |
| emit_diagnostic (diag_kind, loc, 0, |
| "invalid use of brace-enclosed initializer list"); |
| break; |
| } |
| gcc_assert (type == unknown_type_node); |
| if (value && TREE_CODE (value) == COMPONENT_REF) |
| goto bad_member; |
| else if (value && TREE_CODE (value) == ADDR_EXPR) |
| emit_diagnostic (diag_kind, loc, 0, |
| "address of overloaded function with no contextual " |
| "type information"); |
| else if (value && TREE_CODE (value) == OVERLOAD) |
| emit_diagnostic ( |
| diag_kind, loc, 0, |
| "overloaded function with no contextual type information"); |
| else |
| emit_diagnostic ( |
| diag_kind, loc, 0, |
| "insufficient contextual information to determine type"); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| // forked from gcc/cp/decl2.cc decl_constant_var_p |
| |
| /* Nonzero for a VAR_DECL whose value can be used in a constant expression. |
| |
| [expr.const] |
| |
| An integral constant-expression can only involve ... const |
| variables of integral or enumeration types initialized with |
| constant expressions ... |
| |
| C++0x also allows constexpr variables and temporaries initialized |
| with constant expressions. We handle the former here, but the latter |
| are just folded away in cxx_eval_constant_expression. |
| |
| The standard does not require that the expression be non-volatile. |
| G++ implements the proposed correction in DR 457. */ |
| |
| bool |
| decl_constant_var_p (tree decl) |
| { |
| if (!decl_maybe_constant_var_p (decl)) |
| return false; |
| |
| return DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl); |
| } |
| |
| // forked from gcc/cp/decl.cc undeduced_auto_decl |
| |
| /* Returns true iff DECL is a variable or function declared with an auto type |
| that has not yet been deduced to a real type. */ |
| |
| bool undeduced_auto_decl (tree) { return false; } |
| |
| // forked from gcc/cp/decl.cc require_deduced_type |
| |
| /* Complain if DECL has an undeduced return type. */ |
| |
| bool require_deduced_type (tree, tsubst_flags_t) { return true; } |
| |
| /* Return the location of a tree passed to %+ formats. */ |
| |
| location_t |
| location_of (tree t) |
| { |
| if (TYPE_P (t)) |
| { |
| t = TYPE_MAIN_DECL (t); |
| if (t == NULL_TREE) |
| return input_location; |
| } |
| else if (TREE_CODE (t) == OVERLOAD) |
| t = OVL_FIRST (t); |
| |
| if (DECL_P (t)) |
| return DECL_SOURCE_LOCATION (t); |
| |
| return EXPR_LOCATION (t); |
| } |
| |
| /* For element type ELT_TYPE, return the appropriate type of the heap object |
| containing such element(s). COOKIE_SIZE is NULL or the size of cookie |
| in bytes. FULL_SIZE is NULL if it is unknown how big the heap allocation |
| will be, otherwise size of the heap object. If COOKIE_SIZE is NULL, |
| return array type ELT_TYPE[FULL_SIZE / sizeof(ELT_TYPE)], otherwise return |
| struct { size_t[COOKIE_SIZE/sizeof(size_t)]; ELT_TYPE[N]; } |
| where N is nothing (flexible array member) if FULL_SIZE is NULL, otherwise |
| it is computed such that the size of the struct fits into FULL_SIZE. */ |
| |
| tree |
| build_new_constexpr_heap_type (tree elt_type, tree cookie_size, tree full_size) |
| { |
| gcc_assert (cookie_size == NULL_TREE || tree_fits_uhwi_p (cookie_size)); |
| gcc_assert (full_size == NULL_TREE || tree_fits_uhwi_p (full_size)); |
| unsigned HOST_WIDE_INT csz = cookie_size ? tree_to_uhwi (cookie_size) : 0; |
| tree itype2 = NULL_TREE; |
| if (full_size) |
| { |
| unsigned HOST_WIDE_INT fsz = tree_to_uhwi (full_size); |
| gcc_assert (fsz >= csz); |
| fsz -= csz; |
| fsz /= int_size_in_bytes (elt_type); |
| itype2 = build_index_type (size_int (fsz - 1)); |
| if (!cookie_size) |
| return build_cplus_array_type (elt_type, itype2); |
| } |
| else |
| gcc_assert (cookie_size); |
| csz /= int_size_in_bytes (sizetype); |
| tree itype1 = build_index_type (size_int (csz - 1)); |
| tree atype1 = build_cplus_array_type (sizetype, itype1); |
| tree atype2 = build_cplus_array_type (elt_type, itype2); |
| tree rtype = cxx_make_type (RECORD_TYPE); |
| TYPE_NAME (rtype) = heap_identifier; |
| tree fld1 = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, atype1); |
| tree fld2 = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, atype2); |
| DECL_FIELD_CONTEXT (fld1) = rtype; |
| DECL_FIELD_CONTEXT (fld2) = rtype; |
| DECL_ARTIFICIAL (fld1) = true; |
| DECL_ARTIFICIAL (fld2) = true; |
| TYPE_FIELDS (rtype) = fld1; |
| DECL_CHAIN (fld1) = fld2; |
| layout_type (rtype); |
| return rtype; |
| } |
| |
| // forked from gcc/cp/class.cc field_poverlapping_p |
| |
| /* Return true iff FIELD_DECL DECL is potentially overlapping. */ |
| |
| static bool |
| field_poverlapping_p (tree decl) |
| { |
| return lookup_attribute ("no_unique_address", DECL_ATTRIBUTES (decl)); |
| } |
| |
| // forked from gcc/cp/class.cc is_empty_field |
| |
| /* Return true iff DECL is an empty field, either for an empty base or a |
| [[no_unique_address]] data member. */ |
| |
| bool |
| is_empty_field (tree decl) |
| { |
| if (!decl || TREE_CODE (decl) != FIELD_DECL) |
| return false; |
| |
| bool r = (is_empty_class (TREE_TYPE (decl)) && (field_poverlapping_p (decl))); |
| |
| /* Empty fields should have size zero. */ |
| gcc_checking_assert (!r || integer_zerop (DECL_SIZE (decl))); |
| |
| return r; |
| } |
| |
| // forked from gcc/cp/call.cc in_immediate_context |
| |
| /* Return true if in an immediate function context, or an unevaluated operand, |
| or a subexpression of an immediate invocation. */ |
| |
| bool |
| in_immediate_context () |
| { |
| return false; |
| } |
| |
| // forked from gcc/cp/cvt.cc cp_get_fndecl_from_callee |
| |
| /* FN is the callee of a CALL_EXPR or AGGR_INIT_EXPR; return the FUNCTION_DECL |
| if we can. */ |
| |
| tree |
| rs_get_fndecl_from_callee (tree fn, bool fold /* = true */) |
| { |
| if (fn == NULL_TREE) |
| return fn; |
| if (TREE_CODE (fn) == FUNCTION_DECL) |
| return fn; |
| tree type = TREE_TYPE (fn); |
| if (type == NULL_TREE || !INDIRECT_TYPE_P (type)) |
| return NULL_TREE; |
| if (fold) |
| fn = Compile::maybe_constant_init (fn); |
| STRIP_NOPS (fn); |
| if (TREE_CODE (fn) == ADDR_EXPR || TREE_CODE (fn) == FDESC_EXPR) |
| fn = TREE_OPERAND (fn, 0); |
| if (TREE_CODE (fn) == FUNCTION_DECL) |
| return fn; |
| return NULL_TREE; |
| } |
| |
| // forked from gcc/cp/cvt.cc cp_get_callee_fndecl_nofold |
| tree |
| rs_get_callee_fndecl_nofold (tree call) |
| { |
| return rs_get_fndecl_from_callee (cp_get_callee (call), false); |
| } |
| |
| // forked from gcc/cp/init.cc is_class_type |
| |
| /* Report an error if TYPE is not a user-defined, class type. If |
| OR_ELSE is nonzero, give an error message. */ |
| |
| int |
| is_class_type (tree type, int or_else) |
| { |
| if (type == error_mark_node) |
| return 0; |
| |
| if (!CLASS_TYPE_P (type)) |
| { |
| if (or_else) |
| error ("%qT is not a class type", type); |
| return 0; |
| } |
| return 1; |
| } |
| |
| // forked from gcc/cp/decl.cc lookup_enumerator |
| |
| /* Look for an enumerator with the given NAME within the enumeration |
| type ENUMTYPE. This routine is used primarily for qualified name |
| lookup into an enumerator in C++0x, e.g., |
| |
| enum class Color { Red, Green, Blue }; |
| |
| Color color = Color::Red; |
| |
| Returns the value corresponding to the enumerator, or |
| NULL_TREE if no such enumerator was found. */ |
| tree |
| lookup_enumerator (tree enumtype, tree name) |
| { |
| tree e; |
| gcc_assert (enumtype && TREE_CODE (enumtype) == ENUMERAL_TYPE); |
| |
| e = purpose_member (name, TYPE_VALUES (enumtype)); |
| return e ? TREE_VALUE (e) : NULL_TREE; |
| } |
| |
| // forked from gcc/cp/init.cc constant_value_1 |
| // commented out mark_used |
| |
| /* If DECL is a scalar enumeration constant or variable with a |
| constant initializer, return the initializer (or, its initializers, |
| recursively); otherwise, return DECL. If STRICT_P, the |
| initializer is only returned if DECL is a |
| constant-expression. If RETURN_AGGREGATE_CST_OK_P, it is ok to |
| return an aggregate constant. If UNSHARE_P, return an unshared |
| copy of the initializer. */ |
| |
| static tree |
| constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p, |
| bool unshare_p) |
| { |
| while (TREE_CODE (decl) == CONST_DECL || decl_constant_var_p (decl) |
| || (!strict_p && VAR_P (decl) |
| && RS_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (decl)))) |
| { |
| tree init; |
| /* If DECL is a static data member in a template |
| specialization, we must instantiate it here. The |
| initializer for the static data member is not processed |
| until needed; we need it now. */ |
| // mark_used (decl, tf_none); |
| init = DECL_INITIAL (decl); |
| if (init == error_mark_node) |
| { |
| if (TREE_CODE (decl) == CONST_DECL |
| || DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)) |
| /* Treat the error as a constant to avoid cascading errors on |
| excessively recursive template instantiation (c++/9335). */ |
| return init; |
| else |
| return decl; |
| } |
| |
| /* Instantiate a non-dependent initializer for user variables. We |
| mustn't do this for the temporary for an array compound literal; |
| trying to instatiate the initializer will keep creating new |
| temporaries until we crash. Probably it's not useful to do it for |
| other artificial variables, either. */ |
| if (!DECL_ARTIFICIAL (decl)) |
| init = instantiate_non_dependent_or_null (init); |
| if (!init || !TREE_TYPE (init) || !TREE_CONSTANT (init) |
| || (!return_aggregate_cst_ok_p |
| /* Unless RETURN_AGGREGATE_CST_OK_P is true, do not |
| return an aggregate constant (of which string |
| literals are a special case), as we do not want |
| to make inadvertent copies of such entities, and |
| we must be sure that their addresses are the |
| same everywhere. */ |
| && (TREE_CODE (init) == CONSTRUCTOR |
| || TREE_CODE (init) == STRING_CST))) |
| break; |
| /* Don't return a CONSTRUCTOR for a variable with partial run-time |
| initialization, since it doesn't represent the entire value. |
| Similarly for VECTOR_CSTs created by cp_folding those |
| CONSTRUCTORs. */ |
| if ((TREE_CODE (init) == CONSTRUCTOR || TREE_CODE (init) == VECTOR_CST) |
| && !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)) |
| break; |
| /* If the variable has a dynamic initializer, don't use its |
| DECL_INITIAL which doesn't reflect the real value. */ |
| if (VAR_P (decl) && TREE_STATIC (decl) |
| && !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) |
| && DECL_NONTRIVIALLY_INITIALIZED_P (decl)) |
| break; |
| decl = init; |
| } |
| return unshare_p ? unshare_expr (decl) : decl; |
| } |
| |
| // forked from gcc/cp/init.cc decl_constant_value |
| |
| /* A more relaxed version of decl_really_constant_value, used by the |
| common C/C++ code. */ |
| |
| tree |
| decl_constant_value (tree decl, bool unshare_p) |
| { |
| return constant_value_1 (decl, /*strict_p=*/false, |
| /*return_aggregate_cst_ok_p=*/true, |
| /*unshare_p=*/unshare_p); |
| } |
| |
| // Below is forked from gcc/cp/init.cc decl_constant_value |
| |
| tree |
| decl_constant_value (tree decl) |
| { |
| return decl_constant_value (decl, /*unshare_p=*/true); |
| } |
| |
| // Below is forked from gcc/cp/cp-gimplify.cc |
| |
| /* Type for source_location_table hash_set. */ |
| struct GTY ((for_user)) source_location_table_entry |
| { |
| location_t loc; |
| unsigned uid; |
| tree var; |
| }; |
| |
| /* Traits class for function start hash maps below. */ |
| |
| struct source_location_table_entry_hash |
| : ggc_remove<source_location_table_entry> |
| { |
| typedef source_location_table_entry value_type; |
| typedef source_location_table_entry compare_type; |
| |
| static hashval_t hash (const source_location_table_entry &ref) |
| { |
| inchash::hash hstate (0); |
| hstate.add_int (ref.loc); |
| hstate.add_int (ref.uid); |
| return hstate.end (); |
| } |
| |
| static bool equal (const source_location_table_entry &ref1, |
| const source_location_table_entry &ref2) |
| { |
| return ref1.loc == ref2.loc && ref1.uid == ref2.uid; |
| } |
| |
| static void mark_deleted (source_location_table_entry &ref) |
| { |
| ref.loc = UNKNOWN_LOCATION; |
| ref.uid = -1U; |
| ref.var = NULL_TREE; |
| } |
| |
| static const bool empty_zero_p = true; |
| |
| static void mark_empty (source_location_table_entry &ref) |
| { |
| ref.loc = UNKNOWN_LOCATION; |
| ref.uid = 0; |
| ref.var = NULL_TREE; |
| } |
| |
| static bool is_deleted (const source_location_table_entry &ref) |
| { |
| return (ref.loc == UNKNOWN_LOCATION && ref.uid == -1U |
| && ref.var == NULL_TREE); |
| } |
| |
| static bool is_empty (const source_location_table_entry &ref) |
| { |
| return (ref.loc == UNKNOWN_LOCATION && ref.uid == 0 |
| && ref.var == NULL_TREE); |
| } |
| |
| static void pch_nx (source_location_table_entry &p) |
| { |
| extern void gt_pch_nx (source_location_table_entry &); |
| gt_pch_nx (p); |
| } |
| |
| static void pch_nx (source_location_table_entry &p, gt_pointer_operator op, |
| void *cookie) |
| { |
| extern void gt_pch_nx (source_location_table_entry *, gt_pointer_operator, |
| void *); |
| gt_pch_nx (&p, op, cookie); |
| } |
| }; |
| |
| static GTY (()) |
| hash_table<source_location_table_entry_hash> *source_location_table; |
| static GTY (()) unsigned int source_location_id; |
| |
| // Above is forked from gcc/cp/cp-gimplify.cc |
| |
| // forked from gcc/cp/tree.cc lvalue_kind |
| |
| /* If REF is an lvalue, returns the kind of lvalue that REF is. |
| Otherwise, returns clk_none. */ |
| |
| cp_lvalue_kind |
| lvalue_kind (const_tree ref) |
| { |
| cp_lvalue_kind op1_lvalue_kind = clk_none; |
| cp_lvalue_kind op2_lvalue_kind = clk_none; |
| |
| /* Expressions of reference type are sometimes wrapped in |
| INDIRECT_REFs. INDIRECT_REFs are just internal compiler |
| representation, not part of the language, so we have to look |
| through them. */ |
| if (REFERENCE_REF_P (ref)) |
| return lvalue_kind (TREE_OPERAND (ref, 0)); |
| |
| if (TREE_TYPE (ref) && TYPE_REF_P (TREE_TYPE (ref))) |
| { |
| /* unnamed rvalue references are rvalues */ |
| if (TYPE_REF_IS_RVALUE (TREE_TYPE (ref)) && TREE_CODE (ref) != PARM_DECL |
| && !VAR_P (ref) |
| && TREE_CODE (ref) != COMPONENT_REF |
| /* Functions are always lvalues. */ |
| && TREE_CODE (TREE_TYPE (TREE_TYPE (ref))) != FUNCTION_TYPE) |
| { |
| op1_lvalue_kind = clk_rvalueref; |
| if (implicit_rvalue_p (ref)) |
| op1_lvalue_kind |= clk_implicit_rval; |
| return op1_lvalue_kind; |
| } |
| |
| /* lvalue references and named rvalue references are lvalues. */ |
| return clk_ordinary; |
| } |
| |
| if (ref == current_class_ptr) |
| return clk_none; |
| |
| /* Expressions with cv void type are prvalues. */ |
| if (TREE_TYPE (ref) && VOID_TYPE_P (TREE_TYPE (ref))) |
| return clk_none; |
| |
| switch (TREE_CODE (ref)) |
| { |
| case SAVE_EXPR: |
| return clk_none; |
| |
| /* preincrements and predecrements are valid lvals, provided |
| what they refer to are valid lvals. */ |
| case PREINCREMENT_EXPR: |
| case PREDECREMENT_EXPR: |
| case TRY_CATCH_EXPR: |
| case REALPART_EXPR: |
| case IMAGPART_EXPR: |
| case VIEW_CONVERT_EXPR: |
| return lvalue_kind (TREE_OPERAND (ref, 0)); |
| |
| case ARRAY_REF: { |
| tree op1 = TREE_OPERAND (ref, 0); |
| if (TREE_CODE (TREE_TYPE (op1)) == ARRAY_TYPE) |
| { |
| op1_lvalue_kind = lvalue_kind (op1); |
| if (op1_lvalue_kind == clk_class) |
| /* in the case of an array operand, the result is an lvalue if |
| that operand is an lvalue and an xvalue otherwise */ |
| op1_lvalue_kind = clk_rvalueref; |
| return op1_lvalue_kind; |
| } |
| else |
| return clk_ordinary; |
| } |
| |
| case MEMBER_REF: |
| case DOTSTAR_EXPR: |
| if (TREE_CODE (ref) == MEMBER_REF) |
| op1_lvalue_kind = clk_ordinary; |
| else |
| op1_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 0)); |
| if (TYPE_PTRMEMFUNC_P (TREE_TYPE (TREE_OPERAND (ref, 1)))) |
| op1_lvalue_kind = clk_none; |
| else if (op1_lvalue_kind == clk_class) |
| /* 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. */ |
| op1_lvalue_kind = clk_rvalueref; |
| return op1_lvalue_kind; |
| |
| case COMPONENT_REF: |
| op1_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 0)); |
| if (op1_lvalue_kind == clk_class) |
| /* If E1 is an lvalue, then E1.E2 is an lvalue; |
| otherwise E1.E2 is an xvalue. */ |
| op1_lvalue_kind = clk_rvalueref; |
| |
| /* Look at the member designator. */ |
| if (!op1_lvalue_kind) |
| ; |
| else if (is_overloaded_fn (TREE_OPERAND (ref, 1))) |
| /* The "field" can be a FUNCTION_DECL or an OVERLOAD in some |
| situations. If we're seeing a COMPONENT_REF, it's a non-static |
| member, so it isn't an lvalue. */ |
| op1_lvalue_kind = clk_none; |
| else if (TREE_CODE (TREE_OPERAND (ref, 1)) != FIELD_DECL) |
| /* This can be IDENTIFIER_NODE in a template. */; |
| else if (DECL_C_BIT_FIELD (TREE_OPERAND (ref, 1))) |
| { |
| /* Clear the ordinary bit. If this object was a class |
| rvalue we want to preserve that information. */ |
| op1_lvalue_kind &= ~clk_ordinary; |
| /* The lvalue is for a bitfield. */ |
| op1_lvalue_kind |= clk_bitfield; |
| } |
| else if (DECL_PACKED (TREE_OPERAND (ref, 1))) |
| op1_lvalue_kind |= clk_packed; |
| |
| return op1_lvalue_kind; |
| |
| case STRING_CST: |
| case COMPOUND_LITERAL_EXPR: |
| return clk_ordinary; |
| |
| case CONST_DECL: |
| /* CONST_DECL without TREE_STATIC are enumeration values and |
| thus not lvalues. With TREE_STATIC they are used by ObjC++ |
| in objc_build_string_object and need to be considered as |
| lvalues. */ |
| if (!TREE_STATIC (ref)) |
| return clk_none; |
| /* FALLTHRU */ |
| case VAR_DECL: |
| if (VAR_P (ref) && DECL_HAS_VALUE_EXPR_P (ref)) |
| return lvalue_kind (DECL_VALUE_EXPR (CONST_CAST_TREE (ref))); |
| |
| if (TREE_READONLY (ref) && !TREE_STATIC (ref) && DECL_LANG_SPECIFIC (ref) |
| && DECL_IN_AGGR_P (ref)) |
| return clk_none; |
| /* FALLTHRU */ |
| case INDIRECT_REF: |
| case ARROW_EXPR: |
| case PARM_DECL: |
| case RESULT_DECL: |
| case PLACEHOLDER_EXPR: |
| return clk_ordinary; |
| |
| case MAX_EXPR: |
| case MIN_EXPR: |
| /* Disallow <? and >? as lvalues if either argument side-effects. */ |
| if (TREE_SIDE_EFFECTS (TREE_OPERAND (ref, 0)) |
| || TREE_SIDE_EFFECTS (TREE_OPERAND (ref, 1))) |
| return clk_none; |
| op1_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 0)); |
| op2_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 1)); |
| break; |
| |
| case COND_EXPR: { |
| tree op1 = TREE_OPERAND (ref, 1); |
| if (!op1) |
| op1 = TREE_OPERAND (ref, 0); |
| tree op2 = TREE_OPERAND (ref, 2); |
| op1_lvalue_kind = lvalue_kind (op1); |
| op2_lvalue_kind = lvalue_kind (op2); |
| if (!op1_lvalue_kind != !op2_lvalue_kind) |
| { |
| /* The second or the third operand (but not both) is a |
| throw-expression; the result is of the type |
| and value category of the other. */ |
| if (op1_lvalue_kind && TREE_CODE (op2) == THROW_EXPR) |
| op2_lvalue_kind = op1_lvalue_kind; |
| else if (op2_lvalue_kind && TREE_CODE (op1) == THROW_EXPR) |
| op1_lvalue_kind = op2_lvalue_kind; |
| } |
| } |
| break; |
| |
| case MODIFY_EXPR: |
| case TYPEID_EXPR: |
| return clk_ordinary; |
| |
| case COMPOUND_EXPR: |
| return lvalue_kind (TREE_OPERAND (ref, 1)); |
| |
| case TARGET_EXPR: |
| return clk_class; |
| |
| case VA_ARG_EXPR: |
| return (CLASS_TYPE_P (TREE_TYPE (ref)) ? clk_class : clk_none); |
| |
| case CALL_EXPR: |
| /* We can see calls outside of TARGET_EXPR in templates. */ |
| if (CLASS_TYPE_P (TREE_TYPE (ref))) |
| return clk_class; |
| return clk_none; |
| |
| case FUNCTION_DECL: |
| /* All functions (except non-static-member functions) are |
| lvalues. */ |
| return (DECL_NONSTATIC_MEMBER_FUNCTION_P (ref) ? clk_none : clk_ordinary); |
| |
| case NON_DEPENDENT_EXPR: |
| case PAREN_EXPR: |
| return lvalue_kind (TREE_OPERAND (ref, 0)); |
| |
| case TEMPLATE_PARM_INDEX: |
| if (CLASS_TYPE_P (TREE_TYPE (ref))) |
| /* A template parameter object is an lvalue. */ |
| return clk_ordinary; |
| return clk_none; |
| |
| default: |
| if (!TREE_TYPE (ref)) |
| return clk_none; |
| if (CLASS_TYPE_P (TREE_TYPE (ref)) |
| || TREE_CODE (TREE_TYPE (ref)) == ARRAY_TYPE) |
| return clk_class; |
| return clk_none; |
| } |
| |
| /* If one operand is not an lvalue at all, then this expression is |
| not an lvalue. */ |
| if (!op1_lvalue_kind || !op2_lvalue_kind) |
| return clk_none; |
| |
| /* Otherwise, it's an lvalue, and it has all the odd properties |
| contributed by either operand. */ |
| op1_lvalue_kind = op1_lvalue_kind | op2_lvalue_kind; |
| /* It's not an ordinary lvalue if it involves any other kind. */ |
| if ((op1_lvalue_kind & ~clk_ordinary) != clk_none) |
| op1_lvalue_kind &= ~clk_ordinary; |
| /* It can't be both a pseudo-lvalue and a non-addressable lvalue. |
| A COND_EXPR of those should be wrapped in a TARGET_EXPR. */ |
| if ((op1_lvalue_kind & (clk_rvalueref | clk_class)) |
| && (op1_lvalue_kind & (clk_bitfield | clk_packed))) |
| op1_lvalue_kind = clk_none; |
| return op1_lvalue_kind; |
| } |
| |
| // forked from gcc/cp/tree.cc glvalue_p |
| |
| /* This differs from lvalue_p in that xvalues are included. */ |
| |
| bool |
| glvalue_p (const_tree ref) |
| { |
| cp_lvalue_kind kind = lvalue_kind (ref); |
| if (kind & clk_class) |
| return false; |
| else |
| return (kind != clk_none); |
| } |
| |
| // forked from gcc/cp/init.cc cv_qualified_p |
| |
| /* Returns nonzero if TYPE is const or volatile. */ |
| |
| bool |
| cv_qualified_p (const_tree type) |
| { |
| int quals = rs_type_quals (type); |
| return (quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)) != 0; |
| } |
| |
| // forked from gcc/cp/tree.cc rvalue |
| |
| /* EXPR is being used in an rvalue context. Return a version of EXPR |
| that is marked as an rvalue. */ |
| |
| tree |
| rvalue (tree expr) |
| { |
| tree type; |
| |
| if (error_operand_p (expr)) |
| return expr; |
| |
| expr = mark_rvalue_use (expr); |
| |
| /* [basic.lval] |
| |
| Non-class rvalues always have cv-unqualified types. */ |
| type = TREE_TYPE (expr); |
| if (!CLASS_TYPE_P (type) && cv_qualified_p (type)) |
| type = cv_unqualified (type); |
| |
| /* We need to do this for rvalue refs as well to get the right answer |
| from decltype; see c++/36628. */ |
| if (glvalue_p (expr)) |
| { |
| /* But don't use this function for class lvalues; use move (to treat an |
| lvalue as an xvalue) or force_rvalue (to make a prvalue copy). */ |
| gcc_checking_assert (!CLASS_TYPE_P (type)); |
| expr = build1 (NON_LVALUE_EXPR, type, expr); |
| } |
| else if (type != TREE_TYPE (expr)) |
| expr = build_nop (type, expr); |
| |
| return expr; |
| } |
| |
| // forked from gcc/cp/tree.cc bitfield_p |
| |
| /* True if REF is a bit-field. */ |
| |
| bool |
| bitfield_p (const_tree ref) |
| { |
| return (lvalue_kind (ref) & clk_bitfield); |
| } |
| |
| // forked from gcc/cp/typeck.cc cxx_mark_addressable |
| |
| /* Mark EXP saying that we need to be able to take the |
| address of it; it should not be allocated in a register. |
| Value is true if successful. ARRAY_REF_P is true if this |
| is for ARRAY_REF construction - in that case we don't want |
| to look through VIEW_CONVERT_EXPR from VECTOR_TYPE to ARRAY_TYPE, |
| it is fine to use ARRAY_REFs for vector subscripts on vector |
| register variables. |
| |
| C++: we do not allow `current_class_ptr' to be addressable. */ |
| |
| bool |
| cxx_mark_addressable (tree exp, bool array_ref_p) |
| { |
| tree x = exp; |
| |
| while (1) |
| switch (TREE_CODE (x)) |
| { |
| case VIEW_CONVERT_EXPR: |
| if (array_ref_p && TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE |
| && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (x, 0)))) |
| return true; |
| x = TREE_OPERAND (x, 0); |
| break; |
| |
| case COMPONENT_REF: |
| if (bitfield_p (x)) |
| error ("attempt to take address of bit-field"); |
| /* FALLTHRU */ |
| case ADDR_EXPR: |
| case ARRAY_REF: |
| case REALPART_EXPR: |
| case IMAGPART_EXPR: |
| x = TREE_OPERAND (x, 0); |
| break; |
| |
| case PARM_DECL: |
| if (x == current_class_ptr) |
| { |
| error ("cannot take the address of %<this%>, which is an rvalue " |
| "expression"); |
| TREE_ADDRESSABLE (x) = 1; /* so compiler doesn't die later. */ |
| return true; |
| } |
| /* Fall through. */ |
| |
| case VAR_DECL: |
| /* Caller should not be trying to mark initialized |
| constant fields addressable. */ |
| gcc_assert (DECL_LANG_SPECIFIC (x) == 0 || DECL_IN_AGGR_P (x) == 0 |
| || TREE_STATIC (x) || DECL_EXTERNAL (x)); |
| /* Fall through. */ |
| |
| case RESULT_DECL: |
| if (DECL_REGISTER (x) && !TREE_ADDRESSABLE (x) && !DECL_ARTIFICIAL (x)) |
| { |
| if (VAR_P (x) && DECL_HARD_REGISTER (x)) |
| { |
| error ("address of explicit register variable %qD requested", |
| x); |
| return false; |
| } |
| else if (extra_warnings) |
| warning ( |
| OPT_Wextra, |
| "address requested for %qD, which is declared %<register%>", x); |
| } |
| TREE_ADDRESSABLE (x) = 1; |
| return true; |
| |
| case CONST_DECL: |
| case FUNCTION_DECL: |
| TREE_ADDRESSABLE (x) = 1; |
| return true; |
| |
| case CONSTRUCTOR: |
| TREE_ADDRESSABLE (x) = 1; |
| return true; |
| |
| case TARGET_EXPR: |
| TREE_ADDRESSABLE (x) = 1; |
| cxx_mark_addressable (TREE_OPERAND (x, 0)); |
| return true; |
| |
| default: |
| return true; |
| } |
| } |
| |
| // forked from gcc/cp/typeck.cc build_address |
| |
| /* Returns the address of T. This function will fold away |
| ADDR_EXPR of INDIRECT_REF. This is only for low-level usage; |
| most places should use cp_build_addr_expr instead. */ |
| |
| tree |
| build_address (tree t) |
| { |
| if (error_operand_p (t) || !cxx_mark_addressable (t)) |
| return error_mark_node; |
| gcc_checking_assert (TREE_CODE (t) != CONSTRUCTOR); |
| t = build_fold_addr_expr_loc (EXPR_LOCATION (t), t); |
| if (TREE_CODE (t) != ADDR_EXPR) |
| t = rvalue (t); |
| return t; |
| } |
| |
| // forked from gcc/cp/gp-gimplify.cc fold_builtin_source_location |
| |
| /* Fold __builtin_source_location () call. LOC is the location |
| of the call. */ |
| |
| tree |
| fold_builtin_source_location (location_t loc) |
| { |
| // if (source_location_impl == NULL_TREE) |
| // { |
| // auto_diagnostic_group d; |
| // source_location_impl = get_source_location_impl_type (loc); |
| // if (source_location_impl == error_mark_node) |
| // inform (loc, "evaluating %qs", "__builtin_source_location"); |
| // } |
| if (source_location_impl == error_mark_node) |
| return build_zero_cst (const_ptr_type_node); |
| if (source_location_table == NULL) |
| source_location_table |
| = hash_table<source_location_table_entry_hash>::create_ggc (64); |
| const line_map_ordinary *map; |
| source_location_table_entry entry; |
| entry.loc = linemap_resolve_location (line_table, loc, |
| LRK_MACRO_EXPANSION_POINT, &map); |
| entry.uid = current_function_decl ? DECL_UID (current_function_decl) : -1; |
| entry.var = error_mark_node; |
| source_location_table_entry *entryp |
| = source_location_table->find_slot (entry, INSERT); |
| tree var; |
| if (entryp->var) |
| var = entryp->var; |
| else |
| { |
| char tmp_name[32]; |
| ASM_GENERATE_INTERNAL_LABEL (tmp_name, "Lsrc_loc", source_location_id++); |
| var = build_decl (loc, VAR_DECL, get_identifier (tmp_name), |
| source_location_impl); |
| TREE_STATIC (var) = 1; |
| TREE_PUBLIC (var) = 0; |
| DECL_ARTIFICIAL (var) = 1; |
| DECL_IGNORED_P (var) = 1; |
| DECL_EXTERNAL (var) = 0; |
| DECL_DECLARED_CONSTEXPR_P (var) = 1; |
| DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (var) = 1; |
| layout_decl (var, 0); |
| |
| vec<constructor_elt, va_gc> *v = NULL; |
| vec_alloc (v, 4); |
| for (tree field = TYPE_FIELDS (source_location_impl); |
| (field = next_initializable_field (field)) != NULL_TREE; |
| field = DECL_CHAIN (field)) |
| { |
| const char *n = IDENTIFIER_POINTER (DECL_NAME (field)); |
| tree val = NULL_TREE; |
| if (strcmp (n, "_M_file_name") == 0) |
| { |
| if (const char *fname = LOCATION_FILE (loc)) |
| { |
| fname = remap_macro_filename (fname); |
| val = build_string_literal (strlen (fname) + 1, fname); |
| } |
| else |
| val = build_string_literal (1, ""); |
| } |
| else if (strcmp (n, "_M_function_name") == 0) |
| { |
| const char *name = "todo: add funciton name here"; |
| |
| // if (current_function_decl) |
| // name = cxx_printable_name (current_function_decl, 2); |
| |
| val = build_string_literal (strlen (name) + 1, name); |
| } |
| else if (strcmp (n, "_M_line") == 0) |
| val = build_int_cst (TREE_TYPE (field), LOCATION_LINE (loc)); |
| else if (strcmp (n, "_M_column") == 0) |
| val = build_int_cst (TREE_TYPE (field), LOCATION_COLUMN (loc)); |
| else |
| gcc_unreachable (); |
| CONSTRUCTOR_APPEND_ELT (v, field, val); |
| } |
| |
| tree ctor = build_constructor (source_location_impl, v); |
| TREE_CONSTANT (ctor) = 1; |
| TREE_STATIC (ctor) = 1; |
| DECL_INITIAL (var) = ctor; |
| varpool_node::finalize_decl (var); |
| *entryp = entry; |
| entryp->var = var; |
| } |
| |
| return build_fold_addr_expr_with_type_loc (loc, var, const_ptr_type_node); |
| } |
| |
| // forked from gcc/c-family/c-common.cc braced_lists_to_strings |
| |
| /* Attempt to convert a braced array initializer list CTOR for array |
| TYPE into a STRING_CST for convenience and efficiency. Return |
| the converted string on success or the original ctor on failure. */ |
| |
| static tree |
| braced_list_to_string (tree type, tree ctor, bool member) |
| { |
| /* Ignore non-members with unknown size like arrays with unspecified |
| bound. */ |
| tree typesize = TYPE_SIZE_UNIT (type); |
| if (!member && !tree_fits_uhwi_p (typesize)) |
| return ctor; |
| |
| /* If the target char size differes from the host char size, we'd risk |
| loosing data and getting object sizes wrong by converting to |
| host chars. */ |
| if (TYPE_PRECISION (char_type_node) != CHAR_BIT) |
| return ctor; |
| |
| /* If the array has an explicit bound, use it to constrain the size |
| of the string. If it doesn't, be sure to create a string that's |
| as long as implied by the index of the last zero specified via |
| a designator, as in: |
| const char a[] = { [7] = 0 }; */ |
| unsigned HOST_WIDE_INT maxelts; |
| if (typesize) |
| { |
| maxelts = tree_to_uhwi (typesize); |
| maxelts /= tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (type))); |
| } |
| else |
| maxelts = HOST_WIDE_INT_M1U; |
| |
| /* Avoid converting initializers for zero-length arrays (but do |
| create them for flexible array members). */ |
| if (!maxelts) |
| return ctor; |
| |
| unsigned HOST_WIDE_INT nelts = CONSTRUCTOR_NELTS (ctor); |
| |
| auto_vec<char> str; |
| str.reserve (nelts + 1); |
| |
| unsigned HOST_WIDE_INT i; |
| tree index, value; |
| |
| FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), i, index, value) |
| { |
| unsigned HOST_WIDE_INT idx = i; |
| if (index) |
| { |
| if (!tree_fits_uhwi_p (index)) |
| return ctor; |
| idx = tree_to_uhwi (index); |
| } |
| |
| /* auto_vec is limited to UINT_MAX elements. */ |
| if (idx > UINT_MAX) |
| return ctor; |
| |
| /* Avoid non-constant initializers. */ |
| if (!tree_fits_shwi_p (value)) |
| return ctor; |
| |
| /* Skip over embedded nuls except the last one (initializer |
| elements are in ascending order of indices). */ |
| HOST_WIDE_INT val = tree_to_shwi (value); |
| if (!val && i + 1 < nelts) |
| continue; |
| |
| if (idx < str.length ()) |
| return ctor; |
| |
| /* Bail if the CTOR has a block of more than 256 embedded nuls |
| due to implicitly initialized elements. */ |
| unsigned nchars = (idx - str.length ()) + 1; |
| if (nchars > 256) |
| return ctor; |
| |
| if (nchars > 1) |
| { |
| str.reserve (idx); |
| str.quick_grow_cleared (idx); |
| } |
| |
| if (idx >= maxelts) |
| return ctor; |
| |
| str.safe_insert (idx, val); |
| } |
| |
| /* Append a nul string termination. */ |
| if (maxelts != HOST_WIDE_INT_M1U && str.length () < maxelts) |
| str.safe_push (0); |
| |
| /* Build a STRING_CST with the same type as the array. */ |
| tree res = build_string (str.length (), str.begin ()); |
| TREE_TYPE (res) = type; |
| return res; |
| } |
| |
| // forked from gcc/c-family/c-common.cc braced_lists_to_strings |
| |
| /* Implementation of the two-argument braced_lists_to_string withe |
| the same arguments plus MEMBER which is set for struct members |
| to allow initializers for flexible member arrays. */ |
| |
| static tree |
| braced_lists_to_strings (tree type, tree ctor, bool member) |
| { |
| if (TREE_CODE (ctor) != CONSTRUCTOR) |
| return ctor; |
| |
| tree_code code = TREE_CODE (type); |
| |
| tree ttp; |
| if (code == ARRAY_TYPE) |
| ttp = TREE_TYPE (type); |
| else if (code == RECORD_TYPE) |
| { |
| ttp = TREE_TYPE (ctor); |
| if (TREE_CODE (ttp) == ARRAY_TYPE) |
| { |
| type = ttp; |
| ttp = TREE_TYPE (ttp); |
| } |
| } |
| else |
| return ctor; |
| |
| if ((TREE_CODE (ttp) == ARRAY_TYPE || TREE_CODE (ttp) == INTEGER_TYPE) |
| && TYPE_STRING_FLAG (ttp)) |
| return braced_list_to_string (type, ctor, member); |
| |
| code = TREE_CODE (ttp); |
| if (code == ARRAY_TYPE || RECORD_OR_UNION_TYPE_P (ttp)) |
| { |
| bool rec = RECORD_OR_UNION_TYPE_P (ttp); |
| |
| /* Handle array of arrays or struct member initializers. */ |
| tree val; |
| unsigned HOST_WIDE_INT idx; |
| FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (ctor), idx, val) |
| { |
| val = braced_lists_to_strings (ttp, val, rec); |
| CONSTRUCTOR_ELT (ctor, idx)->value = val; |
| } |
| } |
| |
| return ctor; |
| } |
| |
| // forked from gcc/c-family/c-common.cc braced_lists_to_strings |
| |
| /* Attempt to convert a CTOR containing braced array initializer lists |
| for array TYPE into one containing STRING_CSTs, for convenience and |
| efficiency. Recurse for arrays of arrays and member initializers. |
| Return the converted CTOR or STRING_CST on success or the original |
| CTOR otherwise. */ |
| |
| tree |
| braced_lists_to_strings (tree type, tree ctor) |
| { |
| return braced_lists_to_strings (type, ctor, false); |
| } |
| |
| /*--------------------------------------------------------------------------- |
| Constraint satisfaction |
| ---------------------------------------------------------------------------*/ |
| |
| // forked from gcc/cp/constraint.cc satisfying_constraint |
| |
| /* True if we are currently satisfying a failed_type_completions. */ |
| |
| static bool satisfying_constraint; |
| |
| // forked from gcc/cp/constraint.cc satisfying_constraint |
| |
| /* A vector of incomplete types (and of declarations with undeduced return |
| type), appended to by note_failed_type_completion_for_satisfaction. The |
| satisfaction caches use this in order to keep track of "potentially unstable" |
| satisfaction results. |
| |
| Since references to entries in this vector are stored only in the |
| GC-deletable sat_cache, it's safe to make this deletable as well. */ |
| |
| static GTY ((deletable)) vec<tree, va_gc> *failed_type_completions; |
| |
| // forked from gcc/cp/constraint.cc note_failed_type_completion_for_satisfaction |
| |
| /* Called whenever a type completion (or return type deduction) failure occurs |
| that definitely affects the meaning of the program, by e.g. inducing |
| substitution failure. */ |
| |
| void |
| note_failed_type_completion_for_satisfaction (tree t) |
| { |
| if (satisfying_constraint) |
| { |
| gcc_checking_assert ((TYPE_P (t) && !COMPLETE_TYPE_P (t)) |
| || (DECL_P (t) && undeduced_auto_decl (t))); |
| vec_safe_push (failed_type_completions, t); |
| } |
| } |
| |
| // forked from gcc/cp/typeck.cc complete_type |
| |
| /* Try to complete TYPE, if it is incomplete. For example, if TYPE is |
| a template instantiation, do the instantiation. Returns TYPE, |
| whether or not it could be completed, unless something goes |
| horribly wrong, in which case the error_mark_node is returned. */ |
| |
| tree |
| complete_type (tree type) |
| { |
| if (type == NULL_TREE) |
| /* Rather than crash, we return something sure to cause an error |
| at some point. */ |
| return error_mark_node; |
| |
| if (type == error_mark_node || COMPLETE_TYPE_P (type)) |
| ; |
| else if (TREE_CODE (type) == ARRAY_TYPE) |
| { |
| tree t = complete_type (TREE_TYPE (type)); |
| unsigned int needs_constructing, has_nontrivial_dtor; |
| if (COMPLETE_TYPE_P (t)) |
| layout_type (type); |
| needs_constructing = TYPE_NEEDS_CONSTRUCTING (TYPE_MAIN_VARIANT (t)); |
| has_nontrivial_dtor |
| = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TYPE_MAIN_VARIANT (t)); |
| for (t = TYPE_MAIN_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t)) |
| { |
| TYPE_NEEDS_CONSTRUCTING (t) = needs_constructing; |
| TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) = has_nontrivial_dtor; |
| } |
| } |
| |
| return type; |
| } |
| |
| // forked from gcc/cp/typeck.cc complete_type_or_maybe_complain |
| |
| /* Like complete_type, but issue an error if the TYPE cannot be completed. |
| VALUE is used for informative diagnostics. |
| Returns NULL_TREE if the type cannot be made complete. */ |
| |
| tree |
| complete_type_or_maybe_complain (tree type, tree value, tsubst_flags_t complain) |
| { |
| type = complete_type (type); |
| if (type == error_mark_node) |
| /* We already issued an error. */ |
| return NULL_TREE; |
| else if (!COMPLETE_TYPE_P (type)) |
| { |
| if (complain & tf_error) |
| cxx_incomplete_type_diagnostic (value, type, DK_ERROR); |
| note_failed_type_completion_for_satisfaction (type); |
| return NULL_TREE; |
| } |
| else |
| return type; |
| } |
| |
| // forked from gcc/cp/typeck.cc complete_type_or_else |
| |
| tree |
| complete_type_or_else (tree type, tree value) |
| { |
| return complete_type_or_maybe_complain (type, value, tf_warning_or_error); |
| } |
| |
| // forked from gcc/cp/tree.cc std_layout_type_p |
| |
| /* Returns true iff T is a standard-layout type, as defined in |
| [basic.types]. */ |
| |
| bool |
| std_layout_type_p (const_tree t) |
| { |
| t = strip_array_types (CONST_CAST_TREE (t)); |
| |
| if (CLASS_TYPE_P (t)) |
| return !CLASSTYPE_NON_STD_LAYOUT (t); |
| else |
| return scalarish_type_p (t); |
| } |
| |
| // forked from /gcc/cp/semantics.cc first_nonstatic_data_member_p |
| |
| /* Helper function for fold_builtin_is_pointer_inverconvertible_with_class, |
| return true if MEMBERTYPE is the type of the first non-static data member |
| of TYPE or for unions of any members. */ |
| static bool |
| first_nonstatic_data_member_p (tree type, tree membertype) |
| { |
| for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) |
| { |
| if (TREE_CODE (field) != FIELD_DECL) |
| continue; |
| if (DECL_FIELD_IS_BASE (field) && is_empty_field (field)) |
| continue; |
| if (DECL_FIELD_IS_BASE (field)) |
| return first_nonstatic_data_member_p (TREE_TYPE (field), membertype); |
| if (ANON_AGGR_TYPE_P (TREE_TYPE (field))) |
| { |
| if ((TREE_CODE (TREE_TYPE (field)) == UNION_TYPE |
| || std_layout_type_p (TREE_TYPE (field))) |
| && first_nonstatic_data_member_p (TREE_TYPE (field), membertype)) |
| return true; |
| } |
| else if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field), |
| membertype)) |
| return true; |
| if (TREE_CODE (type) != UNION_TYPE) |
| return false; |
| } |
| return false; |
| } |
| |
| // forked from gcc/cp/semantics.cc |
| // fold_builtin_is_pointer_inverconvertible_with_class |
| |
| /* Fold __builtin_is_pointer_interconvertible_with_class call. */ |
| |
| tree |
| fold_builtin_is_pointer_inverconvertible_with_class (location_t loc, int nargs, |
| tree *args) |
| { |
| /* Unless users call the builtin directly, the following 3 checks should be |
| ensured from std::is_pointer_interconvertible_with_class function |
| template. */ |
| if (nargs != 1) |
| { |
| error_at (loc, "%<__builtin_is_pointer_interconvertible_with_class%> " |
| "needs a single argument"); |
| return boolean_false_node; |
| } |
| tree arg = args[0]; |
| if (error_operand_p (arg)) |
| return boolean_false_node; |
| if (!TYPE_PTRMEM_P (TREE_TYPE (arg))) |
| { |
| error_at (loc, "%<__builtin_is_pointer_interconvertible_with_class%> " |
| "argument is not pointer to member"); |
| return boolean_false_node; |
| } |
| |
| if (!TYPE_PTRDATAMEM_P (TREE_TYPE (arg))) |
| return boolean_false_node; |
| |
| tree membertype = TREE_TYPE (TREE_TYPE (arg)); |
| tree basetype = TYPE_OFFSET_BASETYPE (TREE_TYPE (arg)); |
| if (!complete_type_or_else (basetype, NULL_TREE)) |
| return boolean_false_node; |
| |
| if (TREE_CODE (basetype) != UNION_TYPE && !std_layout_type_p (basetype)) |
| return boolean_false_node; |
| |
| if (!first_nonstatic_data_member_p (basetype, membertype)) |
| return boolean_false_node; |
| |
| if (integer_nonzerop (arg)) |
| return boolean_false_node; |
| if (integer_zerop (arg)) |
| return boolean_true_node; |
| |
| return fold_build2 (EQ_EXPR, boolean_type_node, arg, |
| build_zero_cst (TREE_TYPE (arg))); |
| } |
| |
| // forked from gcc/c-family/c-common.cc registered_builtin_types |
| |
| /* Used for communication between c_common_type_for_mode and |
| c_register_builtin_type. */ |
| tree registered_builtin_types; |
| |
| /* Return a data type that has machine mode MODE. |
| If the mode is an integer, |
| then UNSIGNEDP selects between signed and unsigned types. |
| If the mode is a fixed-point mode, |
| then UNSIGNEDP selects between saturating and nonsaturating types. */ |
| |
| // forked from gcc/c-family/c-common.cc c_common_type_for_mode |
| |
| tree |
| c_common_type_for_mode (machine_mode mode, int unsignedp) |
| { |
| tree t; |
| int i; |
| |
| if (mode == TYPE_MODE (integer_type_node)) |
| return unsignedp ? unsigned_type_node : integer_type_node; |
| |
| if (mode == TYPE_MODE (signed_char_type_node)) |
| return unsignedp ? unsigned_char_type_node : signed_char_type_node; |
| |
| if (mode == TYPE_MODE (short_integer_type_node)) |
| return unsignedp ? short_unsigned_type_node : short_integer_type_node; |
| |
| if (mode == TYPE_MODE (long_integer_type_node)) |
| return unsignedp ? long_unsigned_type_node : long_integer_type_node; |
| |
| if (mode == TYPE_MODE (long_long_integer_type_node)) |
| return unsignedp ? long_long_unsigned_type_node |
| : long_long_integer_type_node; |
| |
| for (i = 0; i < NUM_INT_N_ENTS; i++) |
| if (int_n_enabled_p[i] && mode == int_n_data[i].m) |
| return (unsignedp ? int_n_trees[i].unsigned_type |
| : int_n_trees[i].signed_type); |
| |
| if (mode == QImode) |
| return unsignedp ? unsigned_intQI_type_node : intQI_type_node; |
| |
| if (mode == HImode) |
| return unsignedp ? unsigned_intHI_type_node : intHI_type_node; |
| |
| if (mode == SImode) |
| return unsignedp ? unsigned_intSI_type_node : intSI_type_node; |
| |
| if (mode == DImode) |
| return unsignedp ? unsigned_intDI_type_node : intDI_type_node; |
| |
| #if HOST_BITS_PER_WIDE_INT >= 64 |
| if (mode == TYPE_MODE (intTI_type_node)) |
| return unsignedp ? unsigned_intTI_type_node : intTI_type_node; |
| #endif |
| |
| if (mode == TYPE_MODE (float_type_node)) |
| return float_type_node; |
| |
| if (mode == TYPE_MODE (double_type_node)) |
| return double_type_node; |
| |
| if (mode == TYPE_MODE (long_double_type_node)) |
| return long_double_type_node; |
| |
| for (i = 0; i < NUM_FLOATN_NX_TYPES; i++) |
| if (FLOATN_NX_TYPE_NODE (i) != NULL_TREE |
| && mode == TYPE_MODE (FLOATN_NX_TYPE_NODE (i))) |
| return FLOATN_NX_TYPE_NODE (i); |
| |
| if (mode == TYPE_MODE (void_type_node)) |
| return void_type_node; |
| |
| if (mode == TYPE_MODE (build_pointer_type (char_type_node)) |
| || mode == TYPE_MODE (build_pointer_type (integer_type_node))) |
| { |
| unsigned int precision |
| = GET_MODE_PRECISION (as_a<scalar_int_mode> (mode)); |
| return (unsignedp ? make_unsigned_type (precision) |
| : make_signed_type (precision)); |
| } |
| |
| if (COMPLEX_MODE_P (mode)) |
| { |
| machine_mode inner_mode; |
| tree inner_type; |
| |
| if (mode == TYPE_MODE (complex_float_type_node)) |
| return complex_float_type_node; |
| if (mode == TYPE_MODE (complex_double_type_node)) |
| return complex_double_type_node; |
| if (mode == TYPE_MODE (complex_long_double_type_node)) |
| return complex_long_double_type_node; |
| |
| for (i = 0; i < NUM_FLOATN_NX_TYPES; i++) |
| if (COMPLEX_FLOATN_NX_TYPE_NODE (i) != NULL_TREE |
| && mode == TYPE_MODE (COMPLEX_FLOATN_NX_TYPE_NODE (i))) |
| return COMPLEX_FLOATN_NX_TYPE_NODE (i); |
| |
| if (mode == TYPE_MODE (complex_integer_type_node) && !unsignedp) |
| return complex_integer_type_node; |
| |
| inner_mode = GET_MODE_INNER (mode); |
| inner_type = c_common_type_for_mode (inner_mode, unsignedp); |
| if (inner_type != NULL_TREE) |
| return build_complex_type (inner_type); |
| } |
| else if (GET_MODE_CLASS (mode) == MODE_VECTOR_BOOL |
| && valid_vector_subparts_p (GET_MODE_NUNITS (mode))) |
| { |
| unsigned int elem_bits |
| = vector_element_size (GET_MODE_BITSIZE (mode), GET_MODE_NUNITS (mode)); |
| tree bool_type = build_nonstandard_boolean_type (elem_bits); |
| return build_vector_type_for_mode (bool_type, mode); |
| } |
| else if (VECTOR_MODE_P (mode) |
| && valid_vector_subparts_p (GET_MODE_NUNITS (mode))) |
| { |
| machine_mode inner_mode = GET_MODE_INNER (mode); |
| tree inner_type = c_common_type_for_mode (inner_mode, unsignedp); |
| if (inner_type != NULL_TREE) |
| return build_vector_type_for_mode (inner_type, mode); |
| } |
| |
| if (dfloat32_type_node != NULL_TREE && mode == TYPE_MODE (dfloat32_type_node)) |
| return dfloat32_type_node; |
| if (dfloat64_type_node != NULL_TREE && mode == TYPE_MODE (dfloat64_type_node)) |
| return dfloat64_type_node; |
| if (dfloat128_type_node != NULL_TREE |
| && mode == TYPE_MODE (dfloat128_type_node)) |
| return dfloat128_type_node; |
| |
| if (ALL_SCALAR_FIXED_POINT_MODE_P (mode)) |
| { |
| if (mode == TYPE_MODE (short_fract_type_node)) |
| return unsignedp ? sat_short_fract_type_node : short_fract_type_node; |
| if (mode == TYPE_MODE (fract_type_node)) |
| return unsignedp ? sat_fract_type_node : fract_type_node; |
| if (mode == TYPE_MODE (long_fract_type_node)) |
| return unsignedp ? sat_long_fract_type_node : long_fract_type_node; |
| if (mode == TYPE_MODE (long_long_fract_type_node)) |
| return unsignedp ? sat_long_long_fract_type_node |
| : long_long_fract_type_node; |
| |
| if (mode == TYPE_MODE (unsigned_short_fract_type_node)) |
| return unsignedp ? sat_unsigned_short_fract_type_node |
| : unsigned_short_fract_type_node; |
| if (mode == TYPE_MODE (unsigned_fract_type_node)) |
| return unsignedp ? sat_unsigned_fract_type_node |
| : unsigned_fract_type_node; |
| if (mode == TYPE_MODE (unsigned_long_fract_type_node)) |
| return unsignedp ? sat_unsigned_long_fract_type_node |
| : unsigned_long_fract_type_node; |
| if (mode == TYPE_MODE (unsigned_long_long_fract_type_node)) |
| return unsignedp ? sat_unsigned_long_long_fract_type_node |
| : unsigned_long_long_fract_type_node; |
| |
| if (mode == TYPE_MODE (short_accum_type_node)) |
| return unsignedp ? sat_short_accum_type_node : short_accum_type_node; |
| if (mode == TYPE_MODE (accum_type_node)) |
| return unsignedp ? sat_accum_type_node : accum_type_node; |
| if (mode == TYPE_MODE (long_accum_type_node)) |
| return unsignedp ? sat_long_accum_type_node : long_accum_type_node; |
| if (mode == TYPE_MODE (long_long_accum_type_node)) |
| return unsignedp ? sat_long_long_accum_type_node |
| : long_long_accum_type_node; |
| |
| if (mode == TYPE_MODE (unsigned_short_accum_type_node)) |
| return unsignedp ? sat_unsigned_short_accum_type_node |
| : unsigned_short_accum_type_node; |
| if (mode == TYPE_MODE (unsigned_accum_type_node)) |
| return unsignedp ? sat_unsigned_accum_type_node |
| : unsigned_accum_type_node; |
| if (mode == TYPE_MODE (unsigned_long_accum_type_node)) |
| return unsignedp ? sat_unsigned_long_accum_type_node |
| : unsigned_long_accum_type_node; |
| if (mode == TYPE_MODE (unsigned_long_long_accum_type_node)) |
| return unsignedp ? sat_unsigned_long_long_accum_type_node |
| : unsigned_long_long_accum_type_node; |
| |
| if (mode == QQmode) |
| return unsignedp ? sat_qq_type_node : qq_type_node; |
| if (mode == HQmode) |
| return unsignedp ? sat_hq_type_node : hq_type_node; |
| if (mode == SQmode) |
| return unsignedp ? sat_sq_type_node : sq_type_node; |
| if (mode == DQmode) |
| return unsignedp ? sat_dq_type_node : dq_type_node; |
| if (mode == TQmode) |
| return unsignedp ? sat_tq_type_node : tq_type_node; |
| |
| if (mode == UQQmode) |
| return unsignedp ? sat_uqq_type_node : uqq_type_node; |
| if (mode == UHQmode) |
| return unsignedp ? sat_uhq_type_node : uhq_type_node; |
| if (mode == USQmode) |
| return unsignedp ? sat_usq_type_node : usq_type_node; |
| if (mode == UDQmode) |
| return unsignedp ? sat_udq_type_node : udq_type_node; |
| if (mode == UTQmode) |
| return unsignedp ? sat_utq_type_node : utq_type_node; |
| |
| if (mode == HAmode) |
| return unsignedp ? sat_ha_type_node : ha_type_node; |
| if (mode == SAmode) |
| return unsignedp ? sat_sa_type_node : sa_type_node; |
| if (mode == DAmode) |
| return unsignedp ? sat_da_type_node : da_type_node; |
| if (mode == TAmode) |
| return unsignedp ? sat_ta_type_node : ta_type_node; |
| |
| if (mode == UHAmode) |
| return unsignedp ? sat_uha_type_node : uha_type_node; |
| if (mode == USAmode) |
| return unsignedp ? sat_usa_type_node : usa_type_node; |
| if (mode == UDAmode) |
| return unsignedp ? sat_uda_type_node : uda_type_node; |
| if (mode == UTAmode) |
| return unsignedp ? sat_uta_type_node : uta_type_node; |
| } |
| |
| for (t = registered_builtin_types; t; t = TREE_CHAIN (t)) |
| { |
| tree type = TREE_VALUE (t); |
| if (TYPE_MODE (type) == mode |
| && VECTOR_TYPE_P (type) == VECTOR_MODE_P (mode) |
| && !!unsignedp == !!TYPE_UNSIGNED (type)) |
| return type; |
| } |
| return NULL_TREE; |
| } |
| |
| // forked from gcc/cp/semantics.cc finish_underlying_type |
| |
| /* Implement the __underlying_type keyword: Return the underlying |
| type of TYPE, suitable for use as a type-specifier. */ |
| |
| tree |
| finish_underlying_type (tree type) |
| { |
| tree underlying_type; |
| |
| if (!complete_type_or_else (type, NULL_TREE)) |
| return error_mark_node; |
| |
| if (TREE_CODE (type) != ENUMERAL_TYPE) |
| { |
| error ("%qT is not an enumeration type", type); |
| return error_mark_node; |
| } |
| |
| underlying_type = ENUM_UNDERLYING_TYPE (type); |
| |
| /* Fixup necessary in this case because ENUM_UNDERLYING_TYPE |
| includes TYPE_MIN_VALUE and TYPE_MAX_VALUE information. |
| See finish_enum_value_list for details. */ |
| if (!ENUM_FIXED_UNDERLYING_TYPE_P (type)) |
| underlying_type = c_common_type_for_mode (TYPE_MODE (underlying_type), |
| TYPE_UNSIGNED (underlying_type)); |
| |
| return underlying_type; |
| } |
| |
| // forked from gcc/cp/typeck.cc layout_compatible_type_p |
| |
| /* Return true if TYPE1 and TYPE2 are layout-compatible types. */ |
| |
| bool |
| layout_compatible_type_p (tree type1, tree type2) |
| { |
| if (type1 == error_mark_node || type2 == error_mark_node) |
| return false; |
| if (type1 == type2) |
| return true; |
| if (TREE_CODE (type1) != TREE_CODE (type2)) |
| return false; |
| |
| type1 = rs_build_qualified_type (type1, TYPE_UNQUALIFIED); |
| type2 = rs_build_qualified_type (type2, TYPE_UNQUALIFIED); |
| |
| if (TREE_CODE (type1) == ENUMERAL_TYPE) |
| return (TYPE_ALIGN (type1) == TYPE_ALIGN (type2) |
| && tree_int_cst_equal (TYPE_SIZE (type1), TYPE_SIZE (type2)) |
| && same_type_p (finish_underlying_type (type1), |
| finish_underlying_type (type2))); |
| |
| if (CLASS_TYPE_P (type1) && std_layout_type_p (type1) |
| && std_layout_type_p (type2) && TYPE_ALIGN (type1) == TYPE_ALIGN (type2) |
| && tree_int_cst_equal (TYPE_SIZE (type1), TYPE_SIZE (type2))) |
| { |
| tree field1 = TYPE_FIELDS (type1); |
| tree field2 = TYPE_FIELDS (type2); |
| if (TREE_CODE (type1) == RECORD_TYPE) |
| { |
| while (1) |
| { |
| if (!next_common_initial_seqence (field1, field2)) |
| return false; |
| if (field1 == NULL_TREE) |
| return true; |
| field1 = DECL_CHAIN (field1); |
| field2 = DECL_CHAIN (field2); |
| } |
| } |
| /* Otherwise both types must be union types. |
| The standard says: |
| "Two standard-layout unions are layout-compatible if they have |
| the same number of non-static data members and corresponding |
| non-static data members (in any order) have layout-compatible |
| types." |
| but the code anticipates that bitfield vs. non-bitfield, |
| different bitfield widths or presence/absence of |
| [[no_unique_address]] should be checked as well. */ |
| auto_vec<tree, 16> vec; |
| unsigned int count = 0; |
| for (; field1; field1 = DECL_CHAIN (field1)) |
| if (TREE_CODE (field1) == FIELD_DECL) |
| count++; |
| for (; field2; field2 = DECL_CHAIN (field2)) |
| if (TREE_CODE (field2) == FIELD_DECL) |
| vec.safe_push (field2); |
| /* Discussions on core lean towards treating multiple union fields |
| of the same type as the same field, so this might need changing |
| in the future. */ |
| if (count != vec.length ()) |
| return false; |
| for (field1 = TYPE_FIELDS (type1); field1; field1 = DECL_CHAIN (field1)) |
| { |
| if (TREE_CODE (field1) != FIELD_DECL) |
| continue; |
| unsigned int j; |
| tree t1 = DECL_BIT_FIELD_TYPE (field1); |
| if (t1 == NULL_TREE) |
| t1 = TREE_TYPE (field1); |
| FOR_EACH_VEC_ELT (vec, j, field2) |
| { |
| tree t2 = DECL_BIT_FIELD_TYPE (field2); |
| if (t2 == NULL_TREE) |
| t2 = TREE_TYPE (field2); |
| if (DECL_BIT_FIELD_TYPE (field1)) |
| { |
| if (!DECL_BIT_FIELD_TYPE (field2)) |
| continue; |
| if (TYPE_PRECISION (TREE_TYPE (field1)) |
| != TYPE_PRECISION (TREE_TYPE (field2))) |
| continue; |
| } |
| else if (DECL_BIT_FIELD_TYPE (field2)) |
| continue; |
| if (!layout_compatible_type_p (t1, t2)) |
| continue; |
| if ((!lookup_attribute ("no_unique_address", |
| DECL_ATTRIBUTES (field1))) |
| != !lookup_attribute ("no_unique_address", |
| DECL_ATTRIBUTES (field2))) |
| continue; |
| break; |
| } |
| if (j == vec.length ()) |
| return false; |
| vec.unordered_remove (j); |
| } |
| return true; |
| } |
| |
| return same_type_p (type1, type2); |
| } |
| |
| // forked from gcc/cp/semnatics.cc is_corresponding_member_union |
| |
| /* Helper function for is_corresponding_member_aggr. Return true if |
| MEMBERTYPE pointer-to-data-member ARG can be found in anonymous |
| union or structure BASETYPE. */ |
| |
| static bool |
| is_corresponding_member_union (tree basetype, tree membertype, tree arg) |
| { |
| for (tree field = TYPE_FIELDS (basetype); field; field = DECL_CHAIN (field)) |
| if (TREE_CODE (field) != FIELD_DECL || DECL_BIT_FIELD_TYPE (field)) |
| continue; |
| else if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field), |
| membertype)) |
| { |
| if (TREE_CODE (arg) != INTEGER_CST |
| || tree_int_cst_equal (arg, byte_position (field))) |
| return true; |
| } |
| else if (ANON_AGGR_TYPE_P (TREE_TYPE (field))) |
| { |
| tree narg = arg; |
| if (TREE_CODE (basetype) != UNION_TYPE |
| && TREE_CODE (narg) == INTEGER_CST) |
| narg = size_binop (MINUS_EXPR, arg, byte_position (field)); |
| if (is_corresponding_member_union (TREE_TYPE (field), membertype, narg)) |
| return true; |
| } |
| return false; |
| } |
| |
| // forked from gcc/cp/typeck.cc next_common_initial_seqence |
| |
| /* Helper function for layout_compatible_type_p and |
| is_corresponding_member_aggr. Advance to next members (NULL if |
| no further ones) and return true if those members are still part of |
| the common initial sequence. */ |
| |
| bool |
| next_common_initial_seqence (tree &memb1, tree &memb2) |
| { |
| while (memb1) |
| { |
| if (TREE_CODE (memb1) != FIELD_DECL |
| || (DECL_FIELD_IS_BASE (memb1) && is_empty_field (memb1))) |
| { |
| memb1 = DECL_CHAIN (memb1); |
| continue; |
| } |
| if (DECL_FIELD_IS_BASE (memb1)) |
| { |
| memb1 = TYPE_FIELDS (TREE_TYPE (memb1)); |
| continue; |
| } |
| break; |
| } |
| while (memb2) |
| { |
| if (TREE_CODE (memb2) != FIELD_DECL |
| || (DECL_FIELD_IS_BASE (memb2) && is_empty_field (memb2))) |
| { |
| memb2 = DECL_CHAIN (memb2); |
| continue; |
| } |
| if (DECL_FIELD_IS_BASE (memb2)) |
| { |
| memb2 = TYPE_FIELDS (TREE_TYPE (memb2)); |
| continue; |
| } |
| break; |
| } |
| if (memb1 == NULL_TREE && memb2 == NULL_TREE) |
| return true; |
| if (memb1 == NULL_TREE || memb2 == NULL_TREE) |
| return false; |
| if (DECL_BIT_FIELD_TYPE (memb1)) |
| { |
| if (!DECL_BIT_FIELD_TYPE (memb2)) |
| return false; |
| if (!layout_compatible_type_p (DECL_BIT_FIELD_TYPE (memb1), |
| DECL_BIT_FIELD_TYPE (memb2))) |
| return false; |
| if (TYPE_PRECISION (TREE_TYPE (memb1)) |
| != TYPE_PRECISION (TREE_TYPE (memb2))) |
| return false; |
| } |
| else if (DECL_BIT_FIELD_TYPE (memb2)) |
| return false; |
| else if (!layout_compatible_type_p (TREE_TYPE (memb1), TREE_TYPE (memb2))) |
| return false; |
| if ((!lookup_attribute ("no_unique_address", DECL_ATTRIBUTES (memb1))) |
| != !lookup_attribute ("no_unique_address", DECL_ATTRIBUTES (memb2))) |
| return false; |
| if (!tree_int_cst_equal (bit_position (memb1), bit_position (memb2))) |
| return false; |
| return true; |
| } |
| |
| // forked from gcc/cp/semantics.cc is_corresponding_member_aggr |
| |
| /* Helper function for fold_builtin_is_corresponding_member call. |
| Return boolean_false_node if MEMBERTYPE1 BASETYPE1::*ARG1 and |
| MEMBERTYPE2 BASETYPE2::*ARG2 aren't corresponding members, |
| boolean_true_node if they are corresponding members, or for |
| non-constant ARG2 the highest member offset for corresponding |
| members. */ |
| |
| static tree |
| is_corresponding_member_aggr (location_t loc, tree basetype1, tree membertype1, |
| tree arg1, tree basetype2, tree membertype2, |
| tree arg2) |
| { |
| tree field1 = TYPE_FIELDS (basetype1); |
| tree field2 = TYPE_FIELDS (basetype2); |
| tree ret = boolean_false_node; |
| while (1) |
| { |
| bool r = next_common_initial_seqence (field1, field2); |
| if (field1 == NULL_TREE || field2 == NULL_TREE) |
| break; |
| if (r |
| && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field1), |
| membertype1) |
| && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (field2), |
| membertype2)) |
| { |
| tree pos = byte_position (field1); |
| if (TREE_CODE (arg1) == INTEGER_CST && tree_int_cst_equal (arg1, pos)) |
| { |
| if (TREE_CODE (arg2) == INTEGER_CST) |
| return boolean_true_node; |
| return pos; |
| } |
| else if (TREE_CODE (arg1) != INTEGER_CST) |
| ret = pos; |
| } |
| else if (ANON_AGGR_TYPE_P (TREE_TYPE (field1)) |
| && ANON_AGGR_TYPE_P (TREE_TYPE (field2))) |
| { |
| if ((!lookup_attribute ("no_unique_address", |
| DECL_ATTRIBUTES (field1))) |
| != !lookup_attribute ("no_unique_address", |
| DECL_ATTRIBUTES (field2))) |
| break; |
| if (!tree_int_cst_equal (bit_position (field1), |
| bit_position (field2))) |
| break; |
| bool overlap = true; |
| tree pos = byte_position (field1); |
| if (TREE_CODE (arg1) == INTEGER_CST) |
| { |
| tree off1 = fold_convert (sizetype, arg1); |
| tree sz1 = TYPE_SIZE_UNIT (TREE_TYPE (field1)); |
| if (tree_int_cst_lt (off1, pos) |
| || tree_int_cst_le (size_binop (PLUS_EXPR, pos, sz1), off1)) |
| overlap = false; |
| } |
| if (TREE_CODE (arg2) == INTEGER_CST) |
| { |
| tree off2 = fold_convert (sizetype, arg2); |
| tree sz2 = TYPE_SIZE_UNIT (TREE_TYPE (field2)); |
| if (tree_int_cst_lt (off2, pos) |
| || tree_int_cst_le (size_binop (PLUS_EXPR, pos, sz2), off2)) |
| overlap = false; |
| } |
| if (overlap && NON_UNION_CLASS_TYPE_P (TREE_TYPE (field1)) |
| && NON_UNION_CLASS_TYPE_P (TREE_TYPE (field2))) |
| { |
| tree narg1 = arg1; |
| if (TREE_CODE (arg1) == INTEGER_CST) |
| narg1 |
| = size_binop (MINUS_EXPR, fold_convert (sizetype, arg1), pos); |
| tree narg2 = arg2; |
| if (TREE_CODE (arg2) == INTEGER_CST) |
| narg2 |
| = size_binop (MINUS_EXPR, fold_convert (sizetype, arg2), pos); |
| tree t1 = TREE_TYPE (field1); |
| tree t2 = TREE_TYPE (field2); |
| tree nret |
| = is_corresponding_member_aggr (loc, t1, membertype1, narg1, t2, |
| membertype2, narg2); |
| if (nret != boolean_false_node) |
| { |
| if (nret == boolean_true_node) |
| return nret; |
| if (TREE_CODE (arg1) == INTEGER_CST) |
| return size_binop (PLUS_EXPR, nret, pos); |
| ret = size_binop (PLUS_EXPR, nret, pos); |
| } |
| } |
| else if (overlap && TREE_CODE (TREE_TYPE (field1)) == UNION_TYPE |
| && TREE_CODE (TREE_TYPE (field2)) == UNION_TYPE) |
| { |
| tree narg1 = arg1; |
| if (TREE_CODE (arg1) == INTEGER_CST) |
| narg1 |
| = size_binop (MINUS_EXPR, fold_convert (sizetype, arg1), pos); |
| tree narg2 = arg2; |
| if (TREE_CODE (arg2) == INTEGER_CST) |
| narg2 |
| = size_binop (MINUS_EXPR, fold_convert (sizetype, arg2), pos); |
| if (is_corresponding_member_union (TREE_TYPE (field1), |
| membertype1, narg1) |
| && is_corresponding_member_union (TREE_TYPE (field2), |
| membertype2, narg2)) |
| { |
| sorry_at (loc, "%<__builtin_is_corresponding_member%> " |
| "not well defined for anonymous unions"); |
| return boolean_false_node; |
| } |
| } |
| } |
| if (!r) |
| break; |
| field1 = DECL_CHAIN (field1); |
| field2 = DECL_CHAIN (field2); |
| } |
| return ret; |
| } |
| |
| // forked from gcc/cp/call.cc null_member_pointer_value_p |
| |
| /* Returns true iff T is a null member pointer value (4.11). */ |
| |
| bool |
| null_member_pointer_value_p (tree t) |
| { |
| tree type = TREE_TYPE (t); |
| if (!type) |
| return false; |
| else if (TYPE_PTRMEMFUNC_P (type)) |
| return (TREE_CODE (t) == CONSTRUCTOR && CONSTRUCTOR_NELTS (t) |
| && integer_zerop (CONSTRUCTOR_ELT (t, 0)->value)); |
| else if (TYPE_PTRDATAMEM_P (type)) |
| return integer_all_onesp (t); |
| else |
| return false; |
| } |
| |
| // forked from gcc/cp/semantics.cc fold_builtin_is_corresponding_member |
| |
| /* Fold __builtin_is_corresponding_member call. */ |
| |
| tree |
| fold_builtin_is_corresponding_member (location_t loc, int nargs, tree *args) |
| { |
| /* Unless users call the builtin directly, the following 3 checks should be |
| ensured from std::is_corresponding_member function template. */ |
| if (nargs != 2) |
| { |
| error_at (loc, "%<__builtin_is_corresponding_member%> " |
| "needs two arguments"); |
| return boolean_false_node; |
| } |
| tree arg1 = args[0]; |
| tree arg2 = args[1]; |
| if (error_operand_p (arg1) || error_operand_p (arg2)) |
| return boolean_false_node; |
| if (!TYPE_PTRMEM_P (TREE_TYPE (arg1)) || !TYPE_PTRMEM_P (TREE_TYPE (arg2))) |
| { |
| error_at (loc, "%<__builtin_is_corresponding_member%> " |
| "argument is not pointer to member"); |
| return boolean_false_node; |
| } |
| |
| if (!TYPE_PTRDATAMEM_P (TREE_TYPE (arg1)) |
| || !TYPE_PTRDATAMEM_P (TREE_TYPE (arg2))) |
| return boolean_false_node; |
| |
| tree membertype1 = TREE_TYPE (TREE_TYPE (arg1)); |
| tree basetype1 = TYPE_OFFSET_BASETYPE (TREE_TYPE (arg1)); |
| if (!complete_type_or_else (basetype1, NULL_TREE)) |
| return boolean_false_node; |
| |
| tree membertype2 = TREE_TYPE (TREE_TYPE (arg2)); |
| tree basetype2 = TYPE_OFFSET_BASETYPE (TREE_TYPE (arg2)); |
| if (!complete_type_or_else (basetype2, NULL_TREE)) |
| return boolean_false_node; |
| |
| if (!NON_UNION_CLASS_TYPE_P (basetype1) || !NON_UNION_CLASS_TYPE_P (basetype2) |
| || !std_layout_type_p (basetype1) || !std_layout_type_p (basetype2)) |
| return boolean_false_node; |
| |
| /* If the member types aren't layout compatible, then they |
| can't be corresponding members. */ |
| if (!layout_compatible_type_p (membertype1, membertype2)) |
| return boolean_false_node; |
| |
| if (null_member_pointer_value_p (arg1) || null_member_pointer_value_p (arg2)) |
| return boolean_false_node; |
| |
| if (TREE_CODE (arg1) == INTEGER_CST && TREE_CODE (arg2) == INTEGER_CST |
| && !tree_int_cst_equal (arg1, arg2)) |
| return boolean_false_node; |
| |
| if (TREE_CODE (arg2) == INTEGER_CST && TREE_CODE (arg1) != INTEGER_CST) |
| { |
| std::swap (arg1, arg2); |
| std::swap (membertype1, membertype2); |
| std::swap (basetype1, basetype2); |
| } |
| |
| tree ret = is_corresponding_member_aggr (loc, basetype1, membertype1, arg1, |
| basetype2, membertype2, arg2); |
| if (TREE_TYPE (ret) == boolean_type_node) |
| return ret; |
| /* If both arg1 and arg2 are INTEGER_CSTs, is_corresponding_member_aggr |
| already returns boolean_{true,false}_node whether those particular |
| members are corresponding members or not. Otherwise, if only |
| one of them is INTEGER_CST (canonicalized to first being INTEGER_CST |
| above), it returns boolean_false_node if it is certainly not a |
| corresponding member and otherwise we need to do a runtime check that |
| those two OFFSET_TYPE offsets are equal. |
| If neither of the operands is INTEGER_CST, is_corresponding_member_aggr |
| returns the largest offset at which the members would be corresponding |
| members, so perform arg1 <= ret && arg1 == arg2 runtime check. */ |
| gcc_assert (TREE_CODE (arg2) != INTEGER_CST); |
| if (TREE_CODE (arg1) == INTEGER_CST) |
| return fold_build2 (EQ_EXPR, boolean_type_node, arg1, |
| fold_convert (TREE_TYPE (arg1), arg2)); |
| ret = fold_build2 (LE_EXPR, boolean_type_node, |
| fold_convert (pointer_sized_int_node, arg1), |
| fold_convert (pointer_sized_int_node, ret)); |
| return fold_build2 (TRUTH_AND_EXPR, boolean_type_node, ret, |
| fold_build2 (EQ_EXPR, boolean_type_node, arg1, |
| fold_convert (TREE_TYPE (arg1), arg2))); |
| } |
| |
| // forked from gcc/cp/tree.cc lvalue_type |
| |
| /* The type of ARG when used as an lvalue. */ |
| |
| tree |
| lvalue_type (tree arg) |
| { |
| tree type = TREE_TYPE (arg); |
| return type; |
| } |
| |
| // forked from gcc/c-family/c-warn.cc lvalue_error |
| |
| /* Print an error message for an invalid lvalue. USE says |
| how the lvalue is being used and so selects the error message. LOC |
| is the location for the error. */ |
| |
| void |
| lvalue_error (location_t loc, enum lvalue_use use) |
| { |
| switch (use) |
| { |
| case lv_assign: |
| error_at (loc, "lvalue required as left operand of assignment"); |
| break; |
| case lv_increment: |
| error_at (loc, "lvalue required as increment operand"); |
| break; |
| case lv_decrement: |
| error_at (loc, "lvalue required as decrement operand"); |
| break; |
| case lv_addressof: |
| error_at (loc, "lvalue required as unary %<&%> operand"); |
| break; |
| case lv_asm: |
| error_at (loc, "lvalue required in %<asm%> statement"); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| // forked from gcc/cp/cp--gimplify.cc cp_fold_maybe_rvalue |
| |
| /* Fold expression X which is used as an rvalue if RVAL is true. */ |
| |
| tree |
| cp_fold_maybe_rvalue (tree x, bool rval) |
| { |
| while (true) |
| { |
| x = fold (x); |
| if (rval) |
| x = mark_rvalue_use (x); |
| if (rval && DECL_P (x) && !TYPE_REF_P (TREE_TYPE (x))) |
| { |
| tree v = decl_constant_value (x); |
| if (v != x && v != error_mark_node) |
| { |
| x = v; |
| continue; |
| } |
| } |
| break; |
| } |
| return x; |
| } |
| |
| // forked from gcc/cp/cp--gimplify.cc cp_fold_rvalue |
| |
| /* Fold expression X which is used as an rvalue. */ |
| |
| tree |
| cp_fold_rvalue (tree x) |
| { |
| return cp_fold_maybe_rvalue (x, true); |
| } |
| |
| /* Returns true iff class T has a constexpr destructor or has an |
| implicitly declared destructor that we can't tell if it's constexpr |
| without forcing a lazy declaration (which might cause undesired |
| instantiations). */ |
| |
| static bool |
| type_maybe_constexpr_destructor (tree t) |
| { |
| /* Until C++20, only trivial destruction is constexpr. */ |
| if (TYPE_HAS_TRIVIAL_DESTRUCTOR (t)) |
| return true; |
| |
| if (CLASS_TYPE_P (t) && CLASSTYPE_LAZY_DESTRUCTOR (t)) |
| /* Assume it's constexpr. */ |
| return true; |
| tree fn = CLASSTYPE_DESTRUCTOR (t); |
| return (fn && Compile::maybe_constexpr_fn (fn)); |
| } |
| |
| /* T is a non-literal type used in a context which requires a constant |
| expression. Explain why it isn't literal. */ |
| |
| void |
| explain_non_literal_class (tree t) |
| { |
| static hash_set<tree> *diagnosed; |
| |
| if (!CLASS_TYPE_P (t)) |
| return; |
| t = TYPE_MAIN_VARIANT (t); |
| |
| if (diagnosed == NULL) |
| diagnosed = new hash_set<tree>; |
| if (diagnosed->add (t)) |
| /* Already explained. */ |
| return; |
| |
| auto_diagnostic_group d; |
| inform (UNKNOWN_LOCATION, "%q+T is not literal because:", t); |
| if (LAMBDA_TYPE_P (t)) |
| inform (UNKNOWN_LOCATION, |
| " %qT is a closure type, which is only literal in " |
| "C++17 and later", |
| t); |
| else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) |
| && !type_maybe_constexpr_destructor (t)) |
| inform (UNKNOWN_LOCATION, " %q+T does not have %<constexpr%> destructor", |
| t); |
| else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)) |
| inform (UNKNOWN_LOCATION, " %q+T has a non-trivial destructor", t); |
| else if (CLASSTYPE_NON_AGGREGATE (t) && !TYPE_HAS_TRIVIAL_DFLT (t) |
| && !LAMBDA_TYPE_P (t) && !TYPE_HAS_CONSTEXPR_CTOR (t)) |
| { |
| inform (UNKNOWN_LOCATION, |
| " %q+T is not an aggregate, does not have a trivial " |
| "default constructor, and has no %<constexpr%> constructor that " |
| "is not a copy or move constructor", |
| t); |
| if (type_has_non_user_provided_default_constructor (t)) |
| /* Note that we can't simply call locate_ctor because when the |
| constructor is deleted it just returns NULL_TREE. */ |
| for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (t)); iter; ++iter) |
| { |
| tree fn = *iter; |
| tree parms = TYPE_ARG_TYPES (TREE_TYPE (fn)); |
| |
| parms = skip_artificial_parms_for (fn, parms); |
| |
| if (sufficient_parms_p (parms)) |
| { |
| Compile::explain_invalid_constexpr_fn (fn); |
| break; |
| } |
| } |
| } |
| else |
| { |
| tree binfo, base_binfo, field; |
| int i; |
| for (binfo = TYPE_BINFO (t), i = 0; |
| BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) |
| { |
| tree basetype = TREE_TYPE (base_binfo); |
| if (!CLASSTYPE_LITERAL_P (basetype)) |
| { |
| inform (UNKNOWN_LOCATION, |
| " base class %qT of %q+T is non-literal", basetype, t); |
| explain_non_literal_class (basetype); |
| return; |
| } |
| } |
| for (field = TYPE_FIELDS (t); field; field = TREE_CHAIN (field)) |
| { |
| tree ftype; |
| if (TREE_CODE (field) != FIELD_DECL) |
| continue; |
| ftype = TREE_TYPE (field); |
| if (!Compile::literal_type_p (ftype)) |
| { |
| inform (DECL_SOURCE_LOCATION (field), |
| " non-static data member %qD has non-literal type", |
| field); |
| if (CLASS_TYPE_P (ftype)) |
| explain_non_literal_class (ftype); |
| } |
| if (RS_TYPE_VOLATILE_P (ftype)) |
| inform (DECL_SOURCE_LOCATION (field), |
| " non-static data member %qD has volatile type", field); |
| } |
| } |
| } |
| |
| // forked from gcc/cp/call.cc reference_related_p |
| |
| /* Returns nonzero if T1 is reference-related to T2. */ |
| |
| bool |
| reference_related_p (tree t1, tree t2) |
| { |
| if (t1 == error_mark_node || t2 == error_mark_node) |
| return false; |
| |
| t1 = TYPE_MAIN_VARIANT (t1); |
| t2 = TYPE_MAIN_VARIANT (t2); |
| |
| /* [dcl.init.ref] |
| |
| Given types "cv1 T1" and "cv2 T2," "cv1 T1" is reference-related |
| to "cv2 T2" if T1 is similar to T2, or T1 is a base class of T2. */ |
| return (similar_type_p (t1, t2) |
| /*|| (CLASS_TYPE_P (t1) && CLASS_TYPE_P (t2) |
| && DERIVED_FROM_P (t1, t2))*/); |
| } |
| |
| // forked from gcc/cp/typeck2.cc ordinary_char_type_p |
| |
| /* True iff TYPE is a C++20 "ordinary" character type. */ |
| |
| bool |
| ordinary_char_type_p (tree type) |
| { |
| type = TYPE_MAIN_VARIANT (type); |
| return (type == char_type_node || type == signed_char_type_node |
| || type == unsigned_char_type_node); |
| } |
| |
| // forked from gcc/cp/typeck2.cc array_string_literal_compatible_p |
| |
| /* True iff the string literal INIT has a type suitable for initializing array |
| TYPE. */ |
| |
| bool |
| array_string_literal_compatible_p (tree type, tree init) |
| { |
| tree to_char_type = TYPE_MAIN_VARIANT (TREE_TYPE (type)); |
| tree from_char_type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (init))); |
| |
| if (to_char_type == from_char_type) |
| return true; |
| /* The array element type does not match the initializing string |
| literal element type; this is only allowed when both types are |
| ordinary character type. There are no string literals of |
| signed or unsigned char type in the language, but we can get |
| them internally from converting braced-init-lists to |
| STRING_CST. */ |
| if (ordinary_char_type_p (to_char_type) |
| && ordinary_char_type_p (from_char_type)) |
| return true; |
| return false; |
| } |
| |
| } // namespace Rust |