#include "tree-pass.h"
#include "diagnostic.h"
#include "cgraph.h"
+#include "gimple.h"
/* Various flags to control the mangling process. */
typedef enum mangling_flags mangling_flags;
-static tree thunk_adjust (tree, bool, HOST_WIDE_INT, tree);
static void do_build_assign_ref (tree);
static void do_build_copy_constructor (tree);
static tree synthesize_exception_spec (tree, tree (*) (tree, void *), void *);
gcc_assert (TYPE_SIZE (DECL_CONTEXT (function))
&& TYPE_BEING_DEFINED (DECL_CONTEXT (function)));
- thunk = build_decl (FUNCTION_DECL, NULL_TREE, TREE_TYPE (function));
+ thunk = build_decl (DECL_SOURCE_LOCATION (function),
+ 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_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_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;
SET_DECL_ASSEMBLER_NAME (thunk, name);
}
-/* Adjust PTR by the constant FIXED_OFFSET, and by the vtable
- offset indicated by VIRTUAL_OFFSET, if that is
- non-null. THIS_ADJUSTING is nonzero for a this adjusting thunk and
- zero for a result adjusting thunk. */
-
-static tree
-thunk_adjust (tree ptr, bool this_adjusting,
- HOST_WIDE_INT fixed_offset, tree virtual_offset)
-{
- if (this_adjusting)
- /* Adjust the pointer by the constant. */
- ptr = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
- size_int (fixed_offset));
-
- /* If there's a virtual offset, look up that value in the vtable and
- adjust the pointer again. */
- if (virtual_offset)
- {
- tree vtable;
-
- ptr = save_expr (ptr);
- /* The vptr is always at offset zero in the object. */
- vtable = build1 (NOP_EXPR,
- build_pointer_type (build_pointer_type
- (vtable_entry_type)),
- ptr);
- /* Form the vtable address. */
- vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable);
- /* Find the entry with the vcall offset. */
- vtable = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (vtable), vtable,
- fold_convert (sizetype, virtual_offset));
- /* Get the offset itself. */
- vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable);
- /* Adjust the `this' pointer. */
- ptr = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
- fold_convert (sizetype, vtable));
- }
-
- if (!this_adjusting)
- /* Adjust the pointer by the constant. */
- ptr = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
- size_int (fixed_offset));
-
- return ptr;
-}
-
static GTY (()) int thunk_labelno;
-/* Create a static alias to function. */
+/* Create a static alias to target. */
tree
-make_alias_for (tree function, tree newid)
+make_alias_for (tree target, tree newid)
{
- tree alias = build_decl (FUNCTION_DECL, newid, TREE_TYPE (function));
- DECL_LANG_SPECIFIC (alias) = DECL_LANG_SPECIFIC (function);
+ 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 (function);
- TREE_THIS_VOLATILE (alias) = TREE_THIS_VOLATILE (function);
+ TREE_READONLY (alias) = TREE_READONLY (target);
+ TREE_THIS_VOLATILE (alias) = TREE_THIS_VOLATILE (target);
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;
+ 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_NO_STATIC_CHAIN (alias) = 1;
- DECL_PENDING_INLINE_P (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;
+ 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));
alias = make_alias_for (function, get_identifier (buf));
if (!flag_syntax_only)
- assemble_alias (alias, DECL_ASSEMBLER_NAME (function));
+ {
+ bool ok = cgraph_same_body_alias (alias, function);
+ DECL_ASSEMBLER_NAME (function);
+ gcc_assert (ok);
+ }
return alias;
}
DECL_VISIBILITY (thunk_fndecl) = DECL_VISIBILITY (function);
DECL_VISIBILITY_SPECIFIED (thunk_fndecl)
= DECL_VISIBILITY_SPECIFIED (function);
- if (DECL_ONE_ONLY (function))
- make_decl_one_only (thunk_fndecl);
+ if (DECL_ONE_ONLY (function) || DECL_WEAK (function))
+ make_decl_one_only (thunk_fndecl, cxx_comdat_group (thunk_fndecl));
if (flag_syntax_only)
{
}
a = nreverse (t);
DECL_ARGUMENTS (thunk_fndecl) = a;
-
- if (this_adjusting
- && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
- virtual_value, alias))
- {
- const char *fnname;
- tree fn_block;
-
- current_function_decl = thunk_fndecl;
- DECL_RESULT (thunk_fndecl)
- = build_decl (RESULT_DECL, 0, integer_type_node);
- fnname = IDENTIFIER_POINTER (DECL_NAME (thunk_fndecl));
- /* The back end expects DECL_INITIAL to contain a BLOCK, so we
- create one. */
- fn_block = make_node (BLOCK);
- BLOCK_VARS (fn_block) = a;
- DECL_INITIAL (thunk_fndecl) = fn_block;
- init_function_start (thunk_fndecl);
- cfun->is_thunk = 1;
- assemble_start_function (thunk_fndecl, fnname);
-
- targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
- fixed_offset, virtual_value, alias);
-
- assemble_end_function (thunk_fndecl, fnname);
- init_insn_lengths ();
- current_function_decl = 0;
- set_cfun (NULL);
- TREE_ASM_WRITTEN (thunk_fndecl) = 1;
- }
- else
+ TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+ cgraph_add_thunk (thunk_fndecl, function,
+ this_adjusting, fixed_offset, virtual_value,
+ virtual_offset, alias);
+
+ if (!this_adjusting
+ || !targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
+ virtual_value, alias))
{
- int i;
- tree *argarray = (tree *) alloca (list_length (a) * sizeof (tree));
/* If this is a covariant thunk, or we don't have the necessary
code for efficient thunks, generate a thunk function that
just makes a call to the real function. Unfortunately, this
if (varargs_function_p (function))
error ("generic thunk code fails for method %q#D which uses %<...%>",
function);
-
- DECL_RESULT (thunk_fndecl) = NULL_TREE;
-
- start_preparsed_function (thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
- /* We don't bother with a body block for thunks. */
-
- /* There's no need to check accessibility inside the thunk body. */
- push_deferring_access_checks (dk_no_check);
-
- t = a;
- if (this_adjusting)
- t = thunk_adjust (t, /*this_adjusting=*/1,
- fixed_offset, virtual_offset);
-
- /* Build up the call to the real function. */
- argarray[0] = t;
- for (i = 1, a = TREE_CHAIN (a); a; a = TREE_CHAIN (a), i++)
- argarray[i] = a;
- t = build_call_a (alias, i, argarray);
- CALL_FROM_THUNK_P (t) = 1;
- CALL_CANNOT_INLINE_P (t) = 1;
-
- if (VOID_TYPE_P (TREE_TYPE (t)))
- finish_expr_stmt (t);
- else
- {
- if (!this_adjusting)
- {
- tree cond = NULL_TREE;
-
- if (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE)
- {
- /* If the return type is a pointer, we need to
- protect against NULL. We know there will be an
- adjustment, because that's why we're emitting a
- thunk. */
- t = save_expr (t);
- cond = cp_convert (boolean_type_node, t);
- }
-
- t = thunk_adjust (t, /*this_adjusting=*/0,
- fixed_offset, virtual_offset);
- if (cond)
- t = build3 (COND_EXPR, TREE_TYPE (t), cond, t,
- cp_convert (TREE_TYPE (t), integer_zero_node));
- }
- if (MAYBE_CLASS_TYPE_P (TREE_TYPE (t)))
- t = build_cplus_new (TREE_TYPE (t), t);
- finish_return_stmt (t);
- }
-
- /* Since we want to emit the thunk, we explicitly mark its name as
- referenced. */
- mark_decl_referenced (thunk_fndecl);
-
- /* But we don't want debugging information about it. */
- DECL_IGNORED_P (thunk_fndecl) = 1;
-
- /* Re-enable access control. */
- pop_deferring_access_checks ();
-
- thunk_fndecl = finish_function (0);
- cgraph_add_new_function (thunk_fndecl, false);
}
pop_from_top_level ();
\f
/* Code for synthesizing methods which have default semantics defined. */
-/* Generate code for default X(X&) constructor. */
+/* Generate code for default X(X&) or X(X&&) constructor. */
static void
do_build_copy_constructor (tree fndecl)
{
tree parm = FUNCTION_FIRST_USER_PARM (fndecl);
+ bool move_p = DECL_MOVE_CONSTRUCTOR_P (fndecl);
parm = convert_from_reference (parm);
int cvquals = cp_type_quals (TREE_TYPE (parm));
int i;
tree binfo, base_binfo;
+ tree init;
VEC(tree,gc) *vbases;
/* Initialize all the base-classes with the parameter converted
for (vbases = CLASSTYPE_VBASECLASSES (current_class_type), i = 0;
VEC_iterate (tree, vbases, i, binfo); i++)
{
+ init = build_base_path (PLUS_EXPR, parm, binfo, 1);
+ if (move_p)
+ init = move (init);
member_init_list
= tree_cons (binfo,
- build_tree_list (NULL_TREE,
- build_base_path (PLUS_EXPR, parm,
- binfo, 1)),
+ build_tree_list (NULL_TREE, init),
member_init_list);
}
if (BINFO_VIRTUAL_P (base_binfo))
continue;
+ init = build_base_path (PLUS_EXPR, parm, base_binfo, 1);
+ if (move_p)
+ init = move (init);
member_init_list
= tree_cons (base_binfo,
- build_tree_list (NULL_TREE,
- build_base_path (PLUS_EXPR, parm,
- base_binfo, 1)),
+ build_tree_list (NULL_TREE, init),
member_init_list);
}
for (; fields; fields = TREE_CHAIN (fields))
{
- tree init = parm;
tree field = fields;
tree expr_type;
if (DECL_MUTABLE_P (field))
quals &= ~TYPE_QUAL_CONST;
+ quals |= cp_type_quals (expr_type);
expr_type = cp_build_qualified_type (expr_type, quals);
}
- init = build3 (COMPONENT_REF, expr_type, init, field, NULL_TREE);
+ init = build3 (COMPONENT_REF, expr_type, parm, field, NULL_TREE);
+ if (move_p && TREE_CODE (expr_type) != REFERENCE_TYPE)
+ init = move (init);
init = build_tree_list (NULL_TREE, init);
member_init_list = tree_cons (field, init, member_init_list);
it now. */
if (CLASSTYPE_LAZY_COPY_CTOR (type))
lazily_declare_fn (sfk_copy_constructor, type);
+ if (CLASSTYPE_LAZY_MOVE_CTOR (type))
+ lazily_declare_fn (sfk_move_constructor, type);
fns = CLASSTYPE_CONSTRUCTORS (type);
}
else
case sfk_copy_constructor:
case sfk_assignment_operator:
+ case sfk_move_constructor:
{
struct copy_data data;
if (const_p)
{
data.quals = TYPE_QUAL_CONST;
- rhs_parm_type = build_qualified_type (type, TYPE_QUAL_CONST);
+ rhs_parm_type = cp_build_qualified_type (type, TYPE_QUAL_CONST);
}
else
rhs_parm_type = type;
- rhs_parm_type = build_reference_type (rhs_parm_type);
+ rhs_parm_type
+ = cp_build_reference_type (rhs_parm_type,
+ kind == sfk_move_constructor);
parameter_types = tree_cons (NULL_TREE, rhs_parm_type, parameter_types);
raises = synthesize_exception_spec (type, &locate_copy, &data);
break;
fn_type = build_exception_variant (fn_type, raises);
fn = build_lang_decl (FUNCTION_DECL, name, fn_type);
DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (TYPE_NAME (type));
- if (kind == sfk_constructor || kind == sfk_copy_constructor)
+ if (kind == sfk_constructor || kind == sfk_copy_constructor
+ || kind == sfk_move_constructor)
DECL_CONSTRUCTOR_P (fn) = 1;
else if (kind == sfk_destructor)
DECL_DESTRUCTOR_P (fn) = 1;
return fn;
}
+/* Gives any errors about defaulted functions which need to be deferred
+ until the containing class is complete. */
+
+void
+defaulted_late_check (tree fn)
+{
+ /* Complain about invalid signature for defaulted fn. */
+ tree ctx = DECL_CONTEXT (fn);
+ special_function_kind kind = special_function_p (fn);
+ bool fn_const_p = (copy_fn_p (fn) == 2);
+ tree implicit_fn = implicitly_declare_fn (kind, ctx, fn_const_p);
+
+ if (!same_type_p (TREE_TYPE (TREE_TYPE (fn)),
+ TREE_TYPE (TREE_TYPE (implicit_fn)))
+ || !compparms (TYPE_ARG_TYPES (TREE_TYPE (fn)),
+ TYPE_ARG_TYPES (TREE_TYPE (implicit_fn))))
+ {
+ error ("defaulted declaration %q+D", fn);
+ error_at (DECL_SOURCE_LOCATION (fn),
+ "does not match expected signature %qD", implicit_fn);
+ }
+}
+
+/* Returns true iff FN can be explicitly defaulted, and gives any
+ errors if defaulting FN is ill-formed. */
+
+bool
+defaultable_fn_check (tree fn)
+{
+ special_function_kind kind = sfk_none;
+
+ if (DECL_CONSTRUCTOR_P (fn))
+ {
+ if (FUNCTION_FIRST_USER_PARMTYPE (fn) == void_list_node)
+ kind = sfk_constructor;
+ else if (copy_fn_p (fn) > 0
+ && (TREE_CHAIN (FUNCTION_FIRST_USER_PARMTYPE (fn))
+ == void_list_node))
+ kind = sfk_copy_constructor;
+ else if (move_fn_p (fn))
+ kind = sfk_move_constructor;
+ }
+ else if (DECL_DESTRUCTOR_P (fn))
+ kind = sfk_destructor;
+ else if (DECL_ASSIGNMENT_OPERATOR_P (fn)
+ && DECL_OVERLOADED_OPERATOR_P (fn) == NOP_EXPR
+ && copy_fn_p (fn))
+ kind = sfk_assignment_operator;
+
+ if (kind == sfk_none)
+ {
+ error ("%qD cannot be defaulted", fn);
+ return false;
+ }
+ else
+ {
+ tree t = FUNCTION_FIRST_USER_PARMTYPE (fn);
+ for (; t && t != void_list_node; t = TREE_CHAIN (t))
+ if (TREE_PURPOSE (t))
+ {
+ error ("defaulted function %q+D with default argument", fn);
+ break;
+ }
+ if (TYPE_BEING_DEFINED (DECL_CONTEXT (fn)))
+ {
+ if (DECL_NONCONVERTING_P (fn))
+ error ("%qD declared explicit cannot be defaulted in the class "
+ "body", fn);
+ if (current_access_specifier != access_public_node)
+ error ("%qD declared with non-public access cannot be defaulted "
+ "in the class body", fn);
+ if (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)))
+ error ("function %q+D defaulted on its first declaration "
+ "must not have an exception-specification", fn);
+ if (DECL_VIRTUAL_P (fn))
+ error ("%qD declared virtual cannot be defaulted in the class "
+ "body", fn);
+ }
+ else if (!processing_template_decl)
+ defaulted_late_check (fn);
+
+ return true;
+ }
+}
+
/* Add an implicit declaration to TYPE for the kind of function
indicated by SFK. Return the FUNCTION_DECL for the new implicit
declaration. */
/* G++ 3.2 put the implicit destructor at the *beginning* of the
TYPE_METHODS list, which cause the destructor to be emitted
in an incorrect location in the vtable. */
- if (warn_abi && DECL_VIRTUAL_P (fn))
+ if (warn_abi && sfk == sfk_destructor && DECL_VIRTUAL_P (fn))
warning (OPT_Wabi, "vtable layout for class %qT may not be ABI-compliant"
"and may change in a future version of GCC due to "
"implicit virtual destructor",
CLASSTYPE_LAZY_DEFAULT_CTOR (type) = 0;
else if (sfk == sfk_copy_constructor)
CLASSTYPE_LAZY_COPY_CTOR (type) = 0;
+ else if (sfk == sfk_move_constructor)
+ CLASSTYPE_LAZY_MOVE_CTOR (type) = 0;
else if (sfk == sfk_destructor)
CLASSTYPE_LAZY_DESTRUCTOR (type) = 0;
/* Create appropriate clones. */