static tree throw_bad_cast (void) { tree fn = get_identifier ("__cxa_bad_cast"); if (!get_global_value_if_present (fn, &fn)) fn = push_throw_library_fn (fn, build_function_type (ptr_type_node, void_list_node)); return build_cxx_call (fn, NULL_TREE); }
static tree throw_bad_typeid (void) { tree fn = get_identifier ("__cxa_bad_typeid"); if (!get_global_value_if_present (fn, &fn)) { tree t; t = build_reference_type (const_type_info_type_node); t = build_function_type (t, void_list_node); fn = push_throw_library_fn (fn, t); } return build_cxx_call (fn, NULL_TREE); }
static tree build_dynamic_cast_1 (tree type, tree expr) { enum tree_code tc = TREE_CODE (type); tree exprtype = TREE_TYPE (expr); tree dcast_fn; tree old_expr = expr; const char *errstr = NULL; /* T shall be a pointer or reference to a complete class type, or `pointer to cv void''. */ switch (tc) { case POINTER_TYPE: if (TREE_CODE (TREE_TYPE (type)) == VOID_TYPE) break; /* Fall through. */ case REFERENCE_TYPE: if (! IS_AGGR_TYPE (TREE_TYPE (type))) { errstr = "target is not pointer or reference to class"; goto fail; } if (!COMPLETE_TYPE_P (complete_type (TREE_TYPE (type)))) { errstr = "target is not pointer or reference to complete type"; goto fail; } break; default: errstr = "target is not pointer or reference"; goto fail; } if (tc == POINTER_TYPE) { /* If T is a pointer type, v shall be an rvalue of a pointer to complete class type, and the result is an rvalue of type T. */ if (TREE_CODE (exprtype) != POINTER_TYPE) { errstr = "source is not a pointer"; goto fail; } if (! IS_AGGR_TYPE (TREE_TYPE (exprtype))) { errstr = "source is not a pointer to class"; goto fail; } if (!COMPLETE_TYPE_P (complete_type (TREE_TYPE (exprtype)))) { errstr = "source is a pointer to incomplete type"; goto fail; } } else { /* Apply trivial conversion T -> T& for dereferenced ptrs. */ exprtype = build_reference_type (exprtype); expr = convert_to_reference (exprtype, expr, CONV_IMPLICIT, LOOKUP_NORMAL, NULL_TREE); /* T is a reference type, v shall be an lvalue of a complete class type, and the result is an lvalue of the type referred to by T. */ if (! IS_AGGR_TYPE (TREE_TYPE (exprtype))) { errstr = "source is not of class type"; goto fail; } if (!COMPLETE_TYPE_P (complete_type (TREE_TYPE (exprtype)))) { errstr = "source is of incomplete class type"; goto fail; } } /* The dynamic_cast operator shall not cast away constness. */ if (!at_least_as_qualified_p (TREE_TYPE (type), TREE_TYPE (exprtype))) { errstr = "conversion casts away constness"; goto fail; } /* If *type is an unambiguous accessible base class of *exprtype, convert statically. */ { tree binfo; binfo = lookup_base (TREE_TYPE (exprtype), TREE_TYPE (type), ba_check, NULL); if (binfo) { expr = build_base_path (PLUS_EXPR, convert_from_reference (expr), binfo, 0); if (TREE_CODE (exprtype) == POINTER_TYPE) expr = non_lvalue (expr); return expr; } } /* Otherwise *exprtype must be a polymorphic class (have a vtbl). */ if (TYPE_POLYMORPHIC_P (TREE_TYPE (exprtype))) { tree expr1; /* if TYPE is `void *', return pointer to complete object. */ if (tc == POINTER_TYPE && VOID_TYPE_P (TREE_TYPE (type))) { /* if b is an object, dynamic_cast<void *>(&b) == (void *)&b. */ if (TREE_CODE (expr) == ADDR_EXPR && TREE_CODE (TREE_OPERAND (expr, 0)) == VAR_DECL && TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == RECORD_TYPE) return build1 (NOP_EXPR, type, expr); /* Since expr is used twice below, save it. */ expr = save_expr (expr); expr1 = build_headof (expr); if (TREE_TYPE (expr1) != type) expr1 = build1 (NOP_EXPR, type, expr1); return ifnonnull (expr, expr1); } else { tree retval; tree result, td2, td3, elems; tree static_type, target_type, boff; /* If we got here, we can't convert statically. Therefore, dynamic_cast<D&>(b) (b an object) cannot succeed. */ if (tc == REFERENCE_TYPE) { if (TREE_CODE (old_expr) == VAR_DECL && TREE_CODE (TREE_TYPE (old_expr)) == RECORD_TYPE) { tree expr = throw_bad_cast (); warning ("dynamic_cast of %q#D to %q#T can never succeed", old_expr, type); /* Bash it to the expected type. */ TREE_TYPE (expr) = type; return expr; } } /* Ditto for dynamic_cast<D*>(&b). */ else if (TREE_CODE (expr) == ADDR_EXPR) { tree op = TREE_OPERAND (expr, 0); if (TREE_CODE (op) == VAR_DECL && TREE_CODE (TREE_TYPE (op)) == RECORD_TYPE) { warning ("dynamic_cast of %q#D to %q#T can never succeed", op, type); retval = build_int_cst (type, 0); return retval; } } target_type = TYPE_MAIN_VARIANT (TREE_TYPE (type)); static_type = TYPE_MAIN_VARIANT (TREE_TYPE (exprtype)); td2 = get_tinfo_decl (target_type); mark_used (td2); td2 = build_unary_op (ADDR_EXPR, td2, 0); td3 = get_tinfo_decl (static_type); mark_used (td3); td3 = build_unary_op (ADDR_EXPR, td3, 0); /* Determine how T and V are related. */ boff = dcast_base_hint (static_type, target_type); /* Since expr is used twice below, save it. */ expr = save_expr (expr); expr1 = expr; if (tc == REFERENCE_TYPE) expr1 = build_unary_op (ADDR_EXPR, expr1, 0); elems = tree_cons (NULL_TREE, expr1, tree_cons (NULL_TREE, td3, tree_cons (NULL_TREE, td2, tree_cons (NULL_TREE, boff, NULL_TREE)))); dcast_fn = dynamic_cast_node; if (!dcast_fn) { tree tmp; tree tinfo_ptr; tree ns = abi_node; const char *name; push_nested_namespace (ns); tinfo_ptr = xref_tag (class_type, get_identifier ("__class_type_info"), /* APPLE LOCAL 4184203 */ /*tag_scope=*/ts_global, false); tinfo_ptr = build_pointer_type (build_qualified_type (tinfo_ptr, TYPE_QUAL_CONST)); name = "__dynamic_cast"; tmp = tree_cons (NULL_TREE, const_ptr_type_node, tree_cons (NULL_TREE, tinfo_ptr, tree_cons (NULL_TREE, tinfo_ptr, tree_cons (NULL_TREE, ptrdiff_type_node, void_list_node)))); tmp = build_function_type (ptr_type_node, tmp); dcast_fn = build_library_fn_ptr (name, tmp); DECL_IS_PURE (dcast_fn) = 1; pop_nested_namespace (ns); dynamic_cast_node = dcast_fn; } result = build_cxx_call (dcast_fn, elems); if (tc == REFERENCE_TYPE) { tree bad = throw_bad_cast (); result = save_expr (result); return build3 (COND_EXPR, type, result, result, bad); } /* Now back to the type we want from a void*. */ result = cp_convert (type, result); return ifnonnull (expr, result); } } else errstr = "source type is not polymorphic"; fail: error ("cannot dynamic_cast %qE (of type %q#T) to type %q#T (%s)", expr, exprtype, type, errstr); return error_mark_node; }
/* FN is a constructor or destructor, and there are FUNCTION_DECLs cloned from it nearby. Instead of cloning this body, leave it alone and create tiny one-call bodies for the cloned FUNCTION_DECLs. These clones are sibcall candidates, and their resulting code will be very thunk-esque. */ static void thunk_body (tree clone, tree fn, tree clone_to_call) { tree bind, block, call, fn_parm, fn_parm_typelist; int parmno, vtt_parmno; tree clone_parm, parmlist; for (vtt_parmno = -1, parmno = 0, fn_parm = DECL_ARGUMENTS (fn); fn_parm; ++parmno, fn_parm = TREE_CHAIN (fn_parm)) { if (DECL_ARTIFICIAL (fn_parm) && DECL_NAME (fn_parm) == vtt_parm_identifier) { vtt_parmno = parmno; /* Compensate for removed in_charge parameter. */ break; } } /* Currently, we are not supposed to have a vtt argument. */ gcc_assert(vtt_parmno == -1); /* Walk parameter lists together, creating parameter list for call to original function. */ for (parmno = 0, parmlist = NULL, fn_parm = DECL_ARGUMENTS (fn), fn_parm_typelist = TYPE_ARG_TYPES (TREE_TYPE (fn)), clone_parm = DECL_ARGUMENTS (clone); fn_parm; ++parmno, fn_parm = TREE_CHAIN (fn_parm)) { if (parmno == vtt_parmno && ! DECL_HAS_VTT_PARM_P (clone)) { tree typed_null_pointer_node = copy_node (null_pointer_node); gcc_assert (fn_parm_typelist); /* Clobber actual parameter with formal parameter type. */ TREE_TYPE (typed_null_pointer_node) = TREE_VALUE (fn_parm_typelist); parmlist = tree_cons (NULL, typed_null_pointer_node, parmlist); } else if (parmno == 1 && DECL_HAS_IN_CHARGE_PARM_P (fn)) { /* Just skip it. */ } /* Map other parameters to their equivalents in the cloned function. */ else { gcc_assert (clone_parm); DECL_ABSTRACT_ORIGIN (clone_parm) = NULL; parmlist = tree_cons (NULL, clone_parm, parmlist); clone_parm = TREE_CHAIN (clone_parm); } if (fn_parm_typelist) fn_parm_typelist = TREE_CHAIN (fn_parm_typelist); } /* We built this list backwards; fix now. */ parmlist = nreverse (parmlist); TREE_USED (clone_to_call) = 1; call = build_cxx_call (clone_to_call, parmlist); for (parmlist = TREE_OPERAND (call, 1); parmlist; parmlist = TREE_CHAIN (parmlist)) { fn_parm = TREE_VALUE (parmlist); /* Remove the EMPTY_CLASS_EXPR because it upsets estimate_num_insns(). */ if (TREE_CODE (fn_parm) == COMPOUND_EXPR) { gcc_assert (TREE_CODE (TREE_OPERAND (fn_parm, 1)) == EMPTY_CLASS_EXPR); TREE_VALUE (parmlist) = TREE_OPERAND (fn_parm, 0); } } block = make_node (BLOCK); if (targetm.cxx.cdtor_returns_this ()) { tree clone_result = DECL_RESULT (clone); tree modify = build2 (MODIFY_EXPR, TREE_TYPE (clone_result), clone_result, call); add_stmt (modify); BLOCK_VARS (block) = clone_result; } else { add_stmt (call); } bind = c_build_bind_expr (block, cur_stmt_list); DECL_SAVED_TREE (clone) = push_stmt_list (); add_stmt (bind); }