Exemplo n.º 1
0
/*
    declaration:
       block-declaration
       function-definition
       template-declaration
       explicit-instantiation
       explicit-specialization
       linkage-specification
       namespace-definition

    block-declaration:
       simple-declaration
       asm-definition
       namespace-alias-definition
       using-declaration
       using-directive  */
void
pp_cxx_declaration (cxx_pretty_printer *pp, tree t)
{
  if (!DECL_LANG_SPECIFIC (t))
    pp_cxx_simple_declaration (pp, t);
  else if (DECL_USE_TEMPLATE (t))
    switch (DECL_USE_TEMPLATE (t))
      {
      case 1:
	pp_cxx_template_declaration (pp, t);
	break;

      case 2:
	pp_cxx_explicit_specialization (pp, t);
	break;

      case 3:
	pp_cxx_explicit_instantiation (pp, t);
	break;

      default:
	break;
      }
  else switch (TREE_CODE (t))
    {
    case VAR_DECL:
    case TYPE_DECL:
      pp_cxx_simple_declaration (pp, t);
      break;

    case FUNCTION_DECL:
      if (DECL_SAVED_TREE (t))
	pp_cxx_function_definition (pp, t);
      else
	pp_cxx_simple_declaration (pp, t);
      break;

    case NAMESPACE_DECL:
      if (DECL_NAMESPACE_ALIAS (t))
	pp_cxx_namespace_alias_definition (pp, t);
      else
	pp_cxx_original_namespace_definition (pp, t);
      break;

    default:
      pp_unsupported_tree (pp, t);
      break;
    }
}
Exemplo n.º 2
0
tree
make_alias_for (tree function, tree newid)
{
    tree alias = build_decl (FUNCTION_DECL, newid, TREE_TYPE (function));
    DECL_LANG_SPECIFIC (alias) = DECL_LANG_SPECIFIC (function);
    cxx_dup_lang_specific_decl (alias);
    DECL_CONTEXT (alias) = NULL;
    TREE_READONLY (alias) = TREE_READONLY (function);
    TREE_THIS_VOLATILE (alias) = TREE_THIS_VOLATILE (function);
    TREE_PUBLIC (alias) = 0;
    DECL_INTERFACE_KNOWN (alias) = 1;
    DECL_NOT_REALLY_EXTERN (alias) = 1;
    DECL_THIS_STATIC (alias) = 1;
    DECL_SAVED_FUNCTION_DATA (alias) = NULL;
    DECL_DESTRUCTOR_P (alias) = 0;
    DECL_CONSTRUCTOR_P (alias) = 0;
    DECL_CLONED_FUNCTION (alias) = NULL_TREE;
    DECL_EXTERNAL (alias) = 0;
    DECL_ARTIFICIAL (alias) = 1;
    DECL_NO_STATIC_CHAIN (alias) = 1;
    DECL_PENDING_INLINE_P (alias) = 0;
    DECL_INLINE (alias) = 0;
    DECL_DECLARED_INLINE_P (alias) = 0;
    DECL_DEFERRED_FN (alias) = 0;
    DECL_USE_TEMPLATE (alias) = 0;
    DECL_TEMPLATE_INSTANTIATED (alias) = 0;
    DECL_TEMPLATE_INFO (alias) = NULL;
    DECL_INITIAL (alias) = error_mark_node;
    TREE_ADDRESSABLE (alias) = 1;
    TREE_USED (alias) = 1;
    SET_DECL_ASSEMBLER_NAME (alias, DECL_NAME (alias));
    TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (alias)) = 1;
    return alias;
}
Exemplo n.º 3
0
static tree
make_alias_for_thunk (tree function)
{
  tree alias;
  char buf[256];

#if defined (TARGET_IS_PE_COFF)
  if (DECL_ONE_ONLY (function))
    return function;
#endif

  ASM_GENERATE_INTERNAL_LABEL (buf, "LTHUNK", thunk_labelno);
  thunk_labelno++;
  alias = build_decl (FUNCTION_DECL, get_identifier (buf),
		      TREE_TYPE (function));
  DECL_LANG_SPECIFIC (alias) = DECL_LANG_SPECIFIC (function);
  cxx_dup_lang_specific_decl (alias);
  DECL_CONTEXT (alias) = NULL;
  TREE_READONLY (alias) = TREE_READONLY (function);
  TREE_THIS_VOLATILE (alias) = TREE_THIS_VOLATILE (function);
  TREE_PUBLIC (alias) = 0;
  DECL_INTERFACE_KNOWN (alias) = 1;
  DECL_NOT_REALLY_EXTERN (alias) = 1;
  DECL_THIS_STATIC (alias) = 1;
  DECL_SAVED_FUNCTION_DATA (alias) = NULL;
  DECL_DESTRUCTOR_P (alias) = 0;
  DECL_CONSTRUCTOR_P (alias) = 0;
  DECL_CLONED_FUNCTION (alias) = NULL_TREE;
  DECL_EXTERNAL (alias) = 0;
  DECL_ARTIFICIAL (alias) = 1;
  DECL_NO_STATIC_CHAIN (alias) = 1;
  DECL_PENDING_INLINE_P (alias) = 0;
  DECL_INLINE (alias) = 0;
  DECL_DECLARED_INLINE_P (alias) = 0;
  DECL_DEFERRED_FN (alias) = 0;
  DECL_USE_TEMPLATE (alias) = 0;
  DECL_TEMPLATE_INSTANTIATED (alias) = 0;
  DECL_TEMPLATE_INFO (alias) = NULL;
  DECL_INITIAL (alias) = error_mark_node;
  TREE_ADDRESSABLE (alias) = 1;
  TREE_USED (alias) = 1;
  SET_DECL_ASSEMBLER_NAME (alias, DECL_NAME (alias));
  TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (alias)) = 1;
  if (!flag_syntax_only)
    assemble_alias (alias, DECL_ASSEMBLER_NAME (function));
  return alias;
}
Exemplo n.º 4
0
tree
make_alias_for (tree target, tree newid)
{
  tree alias = build_decl (DECL_SOURCE_LOCATION (target),
			   TREE_CODE (target), newid, TREE_TYPE (target));
  DECL_LANG_SPECIFIC (alias) = DECL_LANG_SPECIFIC (target);
  cxx_dup_lang_specific_decl (alias);
  DECL_CONTEXT (alias) = NULL;
  TREE_READONLY (alias) = TREE_READONLY (target);
  TREE_THIS_VOLATILE (alias) = TREE_THIS_VOLATILE (target);
  TREE_PUBLIC (alias) = 0;
  DECL_INTERFACE_KNOWN (alias) = 1;
  if (DECL_LANG_SPECIFIC (alias))
    {
      DECL_NOT_REALLY_EXTERN (alias) = 1;
      DECL_USE_TEMPLATE (alias) = 0;
      DECL_TEMPLATE_INFO (alias) = NULL;
    }
  DECL_EXTERNAL (alias) = 0;
  DECL_ARTIFICIAL (alias) = 1;
  DECL_TEMPLATE_INSTANTIATED (alias) = 0;
  if (TREE_CODE (alias) == FUNCTION_DECL)
    {
      DECL_SAVED_FUNCTION_DATA (alias) = NULL;
      DECL_DESTRUCTOR_P (alias) = 0;
      DECL_CONSTRUCTOR_P (alias) = 0;
      DECL_PENDING_INLINE_P (alias) = 0;
      DECL_DECLARED_INLINE_P (alias) = 0;
      DECL_INITIAL (alias) = error_mark_node;
      DECL_ARGUMENTS (alias) = copy_list (DECL_ARGUMENTS (target));
    }
  else
    TREE_STATIC (alias) = 1;
  TREE_ADDRESSABLE (alias) = 1;
  TREE_USED (alias) = 1;
  SET_DECL_ASSEMBLER_NAME (alias, DECL_NAME (alias));
  TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (alias)) = 1;
  return alias;
}
Exemplo n.º 5
0
tree
do_friend (tree ctype, tree declarator, tree decl,
           tree attrlist, enum overload_flags flags,
           bool funcdef_flag)
{
    gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
    gcc_assert (!ctype || MAYBE_CLASS_TYPE_P (ctype));

    /* Every decl that gets here is a friend of something.  */
    DECL_FRIEND_P (decl) = 1;

    /* Unfortunately, we have to handle attributes here.  Normally we would
       handle them in start_decl_1, but since this is a friend decl start_decl_1
       never gets to see it.  */

    /* Set attributes here so if duplicate decl, will have proper attributes.  */
    cplus_decl_attributes (&decl, attrlist, 0);

    if (TREE_CODE (declarator) == TEMPLATE_ID_EXPR)
    {
        declarator = TREE_OPERAND (declarator, 0);
        if (is_overloaded_fn (declarator))
            declarator = DECL_NAME (get_first_fn (declarator));
    }

    if (ctype)
    {
        /* CLASS_TEMPLATE_DEPTH counts the number of template headers for
        the enclosing class.  FRIEND_DEPTH counts the number of template
         headers used for this friend declaration.  TEMPLATE_MEMBER_P is
         true if a template header in FRIEND_DEPTH is intended for
         DECLARATOR.  For example, the code

           template <class T> struct A {
             template <class U> struct B {
               template <class V> template <class W>
        	 friend void C<V>::f(W);
             };
           };

         will eventually give the following results

         1. CLASS_TEMPLATE_DEPTH equals 2 (for `T' and `U').
         2. FRIEND_DEPTH equals 2 (for `V' and `W').
         3. TEMPLATE_MEMBER_P is true (for `W').  */

        int class_template_depth = template_class_depth (current_class_type);
        int friend_depth = processing_template_decl - class_template_depth;
        /* We will figure this out later.  */
        bool template_member_p = false;

        tree cname = TYPE_NAME (ctype);
        if (TREE_CODE (cname) == TYPE_DECL)
            cname = DECL_NAME (cname);

        /* A method friend.  */
        if (flags == NO_SPECIAL && declarator == cname)
            DECL_CONSTRUCTOR_P (decl) = 1;

        grokclassfn (ctype, decl, flags);

        if (friend_depth)
        {
            if (!uses_template_parms_level (ctype, class_template_depth
                                            + friend_depth))
                template_member_p = true;
        }

        /* A nested class may declare a member of an enclosing class
        to be a friend, so we do lookup here even if CTYPE is in
         the process of being defined.  */
        if (class_template_depth
                || COMPLETE_TYPE_P (ctype)
                || (CLASS_TYPE_P (ctype) && TYPE_BEING_DEFINED (ctype)))
        {
            if (DECL_TEMPLATE_INFO (decl))
                /* DECL is a template specialization.  No need to
                   build a new TEMPLATE_DECL.  */
                ;
            else if (class_template_depth)
                /* We rely on tsubst_friend_function to check the
                   validity of the declaration later.  */
                decl = push_template_decl_real (decl, /*is_friend=*/true);
            else
                decl = check_classfn (ctype, decl,
                                      template_member_p
                                      ? current_template_parms
                                      : NULL_TREE);

            if (template_member_p && decl && TREE_CODE (decl) == FUNCTION_DECL)
                decl = DECL_TI_TEMPLATE (decl);

            if (decl)
                add_friend (current_class_type, decl, /*complain=*/true);
        }
        else
            error ("member %qD declared as friend before type %qT defined",
                   decl, ctype);
    }
    /* A global friend.
       @@ or possibly a friend from a base class ?!?  */
    else if (TREE_CODE (decl) == FUNCTION_DECL)
    {
        int is_friend_template = PROCESSING_REAL_TEMPLATE_DECL_P ();

        /* Friends must all go through the overload machinery,
        even though they may not technically be overloaded.

         Note that because classes all wind up being top-level
         in their scope, their friend wind up in top-level scope as well.  */
        if (funcdef_flag)
            SET_DECL_FRIEND_CONTEXT (decl, current_class_type);

        if (! DECL_USE_TEMPLATE (decl))
        {
            /* We must check whether the decl refers to template
               arguments before push_template_decl_real adds a
               reference to the containing template class.  */
            int warn = (warn_nontemplate_friend
                        && ! funcdef_flag && ! is_friend_template
                        && current_template_parms
                        && uses_template_parms (decl));

            if (is_friend_template
                    || template_class_depth (current_class_type) != 0)
                /* We can't call pushdecl for a template class, since in
                   general, such a declaration depends on template
                   parameters.  Instead, we call pushdecl when the class
                   is instantiated.  */
                decl = push_template_decl_real (decl, /*is_friend=*/true);
            else if (current_function_decl)
            {
                /* This must be a local class.  11.5p11:

                If a friend declaration appears in a local class (9.8) and
                 the name specified is an unqualified name, a prior
                 declaration is looked up without considering scopes that
                 are outside the innermost enclosing non-class scope. For a
                 friend function declaration, if there is no prior
                 declaration, the program is ill-formed.  */
                tree t = lookup_name_innermost_nonclass_level (DECL_NAME (decl));
                if (t)
                    decl = pushdecl_maybe_friend (decl, /*is_friend=*/true);
                else
                {
                    error ("friend declaration %qD in local class without "
                           "prior declaration", decl);
                    return error_mark_node;
                }
            }
            else
            {
                /* We can't use pushdecl, as we might be in a template
                class specialization, and pushdecl will insert an
                 unqualified friend decl into the template parameter
                 scope, rather than the namespace containing it.  */
                tree ns = decl_namespace_context (decl);

                push_nested_namespace (ns);
                decl = pushdecl_namespace_level (decl, /*is_friend=*/true);
                pop_nested_namespace (ns);
            }

            if (warn)
            {
                static int explained;
                bool warned;

                warned = warning (OPT_Wnon_template_friend, "friend declaration "
                                  "%q#D declares a non-template function", decl);
                if (! explained && warned)
                {
                    inform (input_location, "(if this is not what you intended, make sure "
                            "the function template has already been declared "
                            "and add <> after the function name here) ");
                    explained = 1;
                }
            }
        }

        if (decl == error_mark_node)
            return error_mark_node;

        add_friend (current_class_type,
                    is_friend_template ? DECL_TI_TEMPLATE (decl) : decl,
                    /*complain=*/true);
        DECL_FRIEND_P (decl) = 1;
    }

    return decl;
}
Exemplo n.º 6
0
tree
make_thunk (tree function, bool this_adjusting,
            tree fixed_offset, tree virtual_offset)
{
    HOST_WIDE_INT d;
    tree thunk;

    gcc_assert (TREE_CODE (function) == FUNCTION_DECL);
    /* We can have this thunks to covariant thunks, but not vice versa.  */
    gcc_assert (!DECL_THIS_THUNK_P (function));
    gcc_assert (!DECL_RESULT_THUNK_P (function) || this_adjusting);

    /* Scale the VIRTUAL_OFFSET to be in terms of bytes.  */
    if (this_adjusting && virtual_offset)
        virtual_offset
            = size_binop (MULT_EXPR,
                          virtual_offset,
                          convert (ssizetype,
                                   TYPE_SIZE_UNIT (vtable_entry_type)));

    d = tree_low_cst (fixed_offset, 0);

    /* See if we already have the thunk in question.  For this_adjusting
       thunks VIRTUAL_OFFSET will be an INTEGER_CST, for covariant thunks it
       will be a BINFO.  */
    for (thunk = DECL_THUNKS (function); thunk; thunk = TREE_CHAIN (thunk))
        if (DECL_THIS_THUNK_P (thunk) == this_adjusting
                && THUNK_FIXED_OFFSET (thunk) == d
                && !virtual_offset == !THUNK_VIRTUAL_OFFSET (thunk)
                && (!virtual_offset
                    || (this_adjusting
                        ? tree_int_cst_equal (THUNK_VIRTUAL_OFFSET (thunk),
                                              virtual_offset)
                        : THUNK_VIRTUAL_OFFSET (thunk) == virtual_offset)))
            return thunk;

    /* All thunks must be created before FUNCTION is actually emitted;
       the ABI requires that all thunks be emitted together with the
       function to which they transfer control.  */
    gcc_assert (!TREE_ASM_WRITTEN (function));
    /* Likewise, we can only be adding thunks to a function declared in
       the class currently being laid out.  */
    gcc_assert (TYPE_SIZE (DECL_CONTEXT (function))
                && TYPE_BEING_DEFINED (DECL_CONTEXT (function)));

    thunk = build_decl (FUNCTION_DECL, NULL_TREE, TREE_TYPE (function));
    DECL_LANG_SPECIFIC (thunk) = DECL_LANG_SPECIFIC (function);
    cxx_dup_lang_specific_decl (thunk);
    DECL_THUNKS (thunk) = NULL_TREE;

    DECL_CONTEXT (thunk) = DECL_CONTEXT (function);
    TREE_READONLY (thunk) = TREE_READONLY (function);
    TREE_THIS_VOLATILE (thunk) = TREE_THIS_VOLATILE (function);
    TREE_PUBLIC (thunk) = TREE_PUBLIC (function);
    SET_DECL_THUNK_P (thunk, this_adjusting);
    THUNK_TARGET (thunk) = function;
    THUNK_FIXED_OFFSET (thunk) = d;
    THUNK_VIRTUAL_OFFSET (thunk) = virtual_offset;
    THUNK_ALIAS (thunk) = NULL_TREE;

    /* The thunk itself is not a constructor or destructor, even if
       the thing it is thunking to is.  */
    DECL_INTERFACE_KNOWN (thunk) = 1;
    DECL_NOT_REALLY_EXTERN (thunk) = 1;
    DECL_SAVED_FUNCTION_DATA (thunk) = NULL;
    DECL_DESTRUCTOR_P (thunk) = 0;
    DECL_CONSTRUCTOR_P (thunk) = 0;
    DECL_EXTERNAL (thunk) = 1;
    DECL_ARTIFICIAL (thunk) = 1;
    /* Even if this thunk is a member of a local class, we don't
       need a static chain.  */
    DECL_NO_STATIC_CHAIN (thunk) = 1;
    /* The THUNK is not a pending inline, even if the FUNCTION is.  */
    DECL_PENDING_INLINE_P (thunk) = 0;
    DECL_INLINE (thunk) = 0;
    DECL_DECLARED_INLINE_P (thunk) = 0;
    /* Nor has it been deferred.  */
    DECL_DEFERRED_FN (thunk) = 0;
    /* Nor is it a template instantiation.  */
    DECL_USE_TEMPLATE (thunk) = 0;
    DECL_TEMPLATE_INFO (thunk) = NULL;

    /* Add it to the list of thunks associated with FUNCTION.  */
    TREE_CHAIN (thunk) = DECL_THUNKS (function);
    DECL_THUNKS (function) = thunk;

    return thunk;
}
Exemplo n.º 7
0
bool
maybe_clone_body (tree fn)
{
  tree clone;
  bool first = true;
/* APPLE LOCAL begin ARM structor thunks */
  tree clone_to_call;
  struct clone_info info;
/* APPLE LOCAL end ARM structor thunks */

  /* We only clone constructors and destructors.  */
  if (!DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (fn)
      && !DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (fn))
    return 0;

  /* Emit the DWARF1 abstract instance.  */
  (*debug_hooks->deferred_inline_function) (fn);

/* APPLE LOCAL begin ARM structor thunks */
  /* Figure out whether we can use the 'thunk' implementation,
     and if so on which clones. */
  info.next_clone = 0;
  info.which_thunks_ok = compute_use_thunks (fn);
/* APPLE LOCAL end ARM structor thunks */

  /* We know that any clones immediately follow FN in the TYPE_METHODS
     list.  */
  push_to_top_level ();
  FOR_EACH_CLONE (clone, fn)
    {
      tree parm;
      tree clone_parm;
      int parmno;
      splay_tree decl_map;

      /* Update CLONE's source position information to match FN's.  */
      DECL_SOURCE_LOCATION (clone) = DECL_SOURCE_LOCATION (fn);
      DECL_INLINE (clone) = DECL_INLINE (fn);
      DECL_DECLARED_INLINE_P (clone) = DECL_DECLARED_INLINE_P (fn);
      /* LLVM LOCAL begin inlinehint attribute */
      DECL_EXPLICIT_INLINE_P (clone) = DECL_EXPLICIT_INLINE_P (fn);
      /* LLVM LOCAL end inlinehint attribute */
      DECL_COMDAT (clone) = DECL_COMDAT (fn);
      DECL_WEAK (clone) = DECL_WEAK (fn);
      DECL_ONE_ONLY (clone) = DECL_ONE_ONLY (fn);
      DECL_SECTION_NAME (clone) = DECL_SECTION_NAME (fn);
      DECL_USE_TEMPLATE (clone) = DECL_USE_TEMPLATE (fn);
      DECL_EXTERNAL (clone) = DECL_EXTERNAL (fn);
      DECL_INTERFACE_KNOWN (clone) = DECL_INTERFACE_KNOWN (fn);
      DECL_NOT_REALLY_EXTERN (clone) = DECL_NOT_REALLY_EXTERN (fn);
      TREE_PUBLIC (clone) = TREE_PUBLIC (fn);
      DECL_VISIBILITY (clone) = DECL_VISIBILITY (fn);
      DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);

      /* Adjust the parameter names and locations.  */
      parm = DECL_ARGUMENTS (fn);
      clone_parm = DECL_ARGUMENTS (clone);
      /* Update the `this' parameter, which is always first.  */
      update_cloned_parm (parm, clone_parm, first);
      parm = TREE_CHAIN (parm);
      clone_parm = TREE_CHAIN (clone_parm);
      if (DECL_HAS_IN_CHARGE_PARM_P (fn))
	parm = TREE_CHAIN (parm);
      if (DECL_HAS_VTT_PARM_P (fn))
	parm = TREE_CHAIN (parm);
      if (DECL_HAS_VTT_PARM_P (clone))
	clone_parm = TREE_CHAIN (clone_parm);
      for (; parm;
	   parm = TREE_CHAIN (parm), clone_parm = TREE_CHAIN (clone_parm))
	/* Update this parameter.  */
	update_cloned_parm (parm, clone_parm, first);

      /* Start processing the function.  */
      start_preparsed_function (clone, NULL_TREE, SF_PRE_PARSED);

      /* Remap the parameters.  */
      decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
      for (parmno = 0,
	     parm = DECL_ARGUMENTS (fn),
	     clone_parm = DECL_ARGUMENTS (clone);
	   parm;
	   ++parmno,
	     parm = TREE_CHAIN (parm))
	{
	  /* Map the in-charge parameter to an appropriate constant.  */
	  if (DECL_HAS_IN_CHARGE_PARM_P (fn) && parmno == 1)
	    {
	      tree in_charge;
	      in_charge = in_charge_arg_for_name (DECL_NAME (clone));
	      splay_tree_insert (decl_map,
				 (splay_tree_key) parm,
				 (splay_tree_value) in_charge);
	      /* APPLE LOCAL ARM structor thunks */
	      info.in_charge_value [info.next_clone] = in_charge;
	    }
	  else if (DECL_ARTIFICIAL (parm)
		   && DECL_NAME (parm) == vtt_parm_identifier)
	    {
	      /* For a subobject constructor or destructor, the next
		 argument is the VTT parameter.  Remap the VTT_PARM
		 from the CLONE to this parameter.  */
	      if (DECL_HAS_VTT_PARM_P (clone))
		{
		  DECL_ABSTRACT_ORIGIN (clone_parm) = parm;
		  splay_tree_insert (decl_map,
				     (splay_tree_key) parm,
				     (splay_tree_value) clone_parm);
		  clone_parm = TREE_CHAIN (clone_parm);
		}
	      /* Otherwise, map the VTT parameter to `NULL'.  */
	      else
		{
		  splay_tree_insert (decl_map,
				     (splay_tree_key) parm,
				     (splay_tree_value) null_pointer_node);
		}
	    }
	  /* Map other parameters to their equivalents in the cloned
	     function.  */
	  else
	    {
	      splay_tree_insert (decl_map,
				 (splay_tree_key) parm,
				 (splay_tree_value) clone_parm);
	      clone_parm = TREE_CHAIN (clone_parm);
	    }
	}

      if (targetm.cxx.cdtor_returns_this ())
	{
	  parm = DECL_RESULT (fn);
	  clone_parm = DECL_RESULT (clone);
	  splay_tree_insert (decl_map, (splay_tree_key) parm,
			     (splay_tree_value) clone_parm);
	}
      /* APPLE LOCAL begin ARM structor thunks */
      clone_to_call = find_earlier_clone (&info);
      if (clone_to_call)
	/* Bodies are identical; replace later one with call to an
	   earlier one. */
	thunk_body (clone, fn, clone_to_call);
      else
	/* Clone the body.  */
	clone_body (clone, fn, decl_map);
      /* APPLE LOCAL end ARM structor thunks */

      /* The clone can throw iff the original function can throw.  */
      cp_function_chain->can_throw = !TREE_NOTHROW (fn);

      /* Now, expand this function into RTL, if appropriate.  */
      finish_function (0);
      BLOCK_ABSTRACT_ORIGIN (DECL_INITIAL (clone)) = DECL_INITIAL (fn);
      expand_or_defer_fn (clone);
      first = false;
      /* APPLE LOCAL begin ARM structor thunks */
      info.clones [info.next_clone] = clone;
      info.next_clone++;
      /* APPLE LOCAL end ARM structor thunks */
    }