X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Fcp%2Fmethod.c;h=359e71d1b32c9a3a2985ce453822fa92c447b6af;hp=3ef73fb8d0479b8b3231c2efd46f8e14ffc27b1c;hb=48148244971e4e4bcc2fb879e007afb23db72052;hpb=c4610908aa906d9c035fac6875fbb8a7c444d7f3 diff --git a/gcc/cp/method.c b/gcc/cp/method.c index 3ef73fb8d04..359e71d1b32 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -1,7 +1,7 @@ /* Handle the hair of processing (but not expanding) inline functions. Also manage function and variable name overloading. Copyright (C) 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007 + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009 Free Software Foundation, Inc. Contributed by Michael Tiemann (tiemann@cygnus.com) @@ -29,8 +29,6 @@ along with GCC; see the file COPYING3. If not see #include "tm.h" #include "tree.h" #include "cp-tree.h" -#include "rtl.h" -#include "expr.h" #include "output.h" #include "flags.h" #include "toplev.h" @@ -38,6 +36,8 @@ along with GCC; see the file COPYING3. If not see #include "target.h" #include "tree-pass.h" #include "diagnostic.h" +#include "cgraph.h" +#include "gimple.h" /* Various flags to control the mangling process. */ @@ -57,10 +57,8 @@ enum mangling_flags 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_assign (tree); static void do_build_copy_constructor (tree); -static tree synthesize_exception_spec (tree, tree (*) (tree, void *), void *); static tree make_alias_for_thunk (tree); /* Called once to initialize method.c. */ @@ -105,7 +103,7 @@ make_thunk (tree function, bool this_adjusting, /* 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)) + for (thunk = DECL_THUNKS (function); thunk; thunk = DECL_CHAIN (thunk)) if (DECL_THIS_THUNK_P (thunk) == this_adjusting && THUNK_FIXED_OFFSET (thunk) == d && !virtual_offset == !THUNK_VIRTUAL_OFFSET (thunk) @@ -125,7 +123,8 @@ make_thunk (tree function, bool this_adjusting, 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; @@ -149,21 +148,15 @@ make_thunk (tree function, bool this_adjusting, 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_CHAIN (thunk) = DECL_THUNKS (function); DECL_THUNKS (function) = thunk; return thunk; @@ -195,7 +188,7 @@ finish_thunk (tree thunk) tree cov_probe; for (cov_probe = DECL_THUNKS (function); - cov_probe; cov_probe = TREE_CHAIN (cov_probe)) + cov_probe; cov_probe = DECL_CHAIN (cov_probe)) if (DECL_NAME (cov_probe) == name) { gcc_assert (!DECL_THUNKS (thunk)); @@ -209,84 +202,43 @@ finish_thunk (tree thunk) 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_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; + 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)); @@ -306,7 +258,11 @@ make_alias_for_thunk (tree function) 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; } @@ -381,8 +337,8 @@ use_thunk (tree thunk_fndecl, bool emit_p) 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) { @@ -408,51 +364,26 @@ use_thunk (tree thunk_fndecl, bool emit_p) /* Set up cloned argument trees for the thunk. */ t = NULL_TREE; - for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a)) + for (a = DECL_ARGUMENTS (function); a; a = DECL_CHAIN (a)) { tree x = copy_node (a); - TREE_CHAIN (x) = t; + DECL_CHAIN (x) = t; DECL_CONTEXT (x) = thunk_fndecl; - SET_DECL_RTL (x, NULL_RTX); + SET_DECL_RTL (x, NULL); DECL_HAS_VALUE_EXPR_P (x) = 0; t = x; } 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)) + 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)) { - 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); - current_function_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 - { - 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 @@ -461,91 +392,97 @@ use_thunk (tree thunk_fndecl, bool emit_p) 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); + pop_from_top_level (); +} + +/* Code for synthesizing methods which have default semantics defined. */ - /* 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; +/* True iff CTYPE has a trivial SFK. */ - 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 (IS_AGGR_TYPE (TREE_TYPE (t))) - t = build_cplus_new (TREE_TYPE (t), t); - finish_return_stmt (t); - } +static bool +type_has_trivial_fn (tree ctype, special_function_kind sfk) +{ + switch (sfk) + { + case sfk_constructor: + return !TYPE_HAS_COMPLEX_DFLT (ctype); + case sfk_copy_constructor: + return !TYPE_HAS_COMPLEX_COPY_CTOR (ctype); + case sfk_move_constructor: + return !TYPE_HAS_COMPLEX_MOVE_CTOR (ctype); + case sfk_copy_assignment: + return !TYPE_HAS_COMPLEX_COPY_ASSIGN (ctype); + case sfk_move_assignment: + return !TYPE_HAS_COMPLEX_MOVE_ASSIGN (ctype); + case sfk_destructor: + return !TYPE_HAS_NONTRIVIAL_DESTRUCTOR (ctype); + default: + gcc_unreachable (); + } +} - /* Since we want to emit the thunk, we explicitly mark its name as - referenced. */ - mark_decl_referenced (thunk_fndecl); +/* Note that CTYPE has a non-trivial SFK even though we previously thought + it was trivial. */ - /* But we don't want debugging information about it. */ - DECL_IGNORED_P (thunk_fndecl) = 1; +static void +type_set_nontrivial_flag (tree ctype, special_function_kind sfk) +{ + switch (sfk) + { + case sfk_constructor: + TYPE_HAS_COMPLEX_DFLT (ctype) = true; + return; + case sfk_copy_constructor: + TYPE_HAS_COMPLEX_COPY_CTOR (ctype) = true; + return; + case sfk_move_constructor: + TYPE_HAS_COMPLEX_MOVE_CTOR (ctype) = true; + return; + case sfk_copy_assignment: + TYPE_HAS_COMPLEX_COPY_ASSIGN (ctype) = true; + return; + case sfk_move_assignment: + TYPE_HAS_COMPLEX_MOVE_ASSIGN (ctype) = true; + return; + case sfk_destructor: + TYPE_HAS_NONTRIVIAL_DESTRUCTOR (ctype) = true; + return; + default: + gcc_unreachable (); + } +} - /* Re-enable access control. */ - pop_deferring_access_checks (); +/* True iff FN is a trivial defaulted member function ([cd]tor, op=). */ - thunk_fndecl = finish_function (0); - tree_lowering_passes (thunk_fndecl); - tree_rest_of_compilation (thunk_fndecl); - } +bool +trivial_fn_p (tree fn) +{ + if (!DECL_DEFAULTED_FN (fn)) + return false; - pop_from_top_level (); + /* If fn is a clone, get the primary variant. */ + fn = DECL_ORIGIN (fn); + return type_has_trivial_fn (DECL_CONTEXT (fn), special_function_p (fn)); } - -/* 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); + bool trivial = trivial_fn_p (fndecl); parm = convert_from_reference (parm); - if (TYPE_HAS_TRIVIAL_INIT_REF (current_class_type) + if (trivial && is_empty_class (current_class_type)) /* Don't copy the padding byte; it might not have been allocated if *this is a base subobject. */; - else if (TYPE_HAS_TRIVIAL_INIT_REF (current_class_type)) + else if (trivial) { tree t = build2 (INIT_EXPR, void_type_node, current_class_ref, parm); finish_expr_stmt (t); @@ -557,6 +494,7 @@ do_build_copy_constructor (tree fndecl) 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 @@ -567,11 +505,12 @@ do_build_copy_constructor (tree fndecl) 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); } @@ -581,17 +520,17 @@ do_build_copy_constructor (tree fndecl) 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)) + for (; fields; fields = DECL_CHAIN (fields)) { - tree init = parm; tree field = fields; tree expr_type; @@ -606,7 +545,8 @@ do_build_copy_constructor (tree fndecl) } else if (ANON_AGGR_TYPE_P (expr_type) && TYPE_FIELDS (expr_type)) /* Just use the field; anonymous types can't have - nontrivial copy ctors or assignment ops. */; + nontrivial copy ctors or assignment ops or this + function would be deleted. */; else continue; @@ -621,10 +561,13 @@ do_build_copy_constructor (tree fndecl) 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); @@ -634,19 +577,21 @@ do_build_copy_constructor (tree fndecl) } static void -do_build_assign_ref (tree fndecl) +do_build_copy_assign (tree fndecl) { - tree parm = TREE_CHAIN (DECL_ARGUMENTS (fndecl)); + tree parm = DECL_CHAIN (DECL_ARGUMENTS (fndecl)); tree compound_stmt; + bool move_p = move_fn_p (fndecl); + bool trivial = trivial_fn_p (fndecl); compound_stmt = begin_compound_stmt (0); parm = convert_from_reference (parm); - if (TYPE_HAS_TRIVIAL_ASSIGN_REF (current_class_type) + if (trivial && is_empty_class (current_class_type)) /* Don't copy the padding byte; it might not have been allocated if *this is a base subobject. */; - else if (TYPE_HAS_TRIVIAL_ASSIGN_REF (current_class_type)) + else if (trivial) { tree t = build2 (MODIFY_EXPR, void_type_node, current_class_ref, parm); finish_expr_stmt (t); @@ -663,24 +608,29 @@ do_build_assign_ref (tree fndecl) BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) { tree converted_parm; + VEC(tree,gc) *parmvec; /* We must convert PARM directly to the base class explicitly since the base class may be ambiguous. */ converted_parm = build_base_path (PLUS_EXPR, parm, base_binfo, 1); + if (move_p) + converted_parm = move (converted_parm); /* Call the base class assignment operator. */ + parmvec = make_tree_vector_single (converted_parm); finish_expr_stmt (build_special_member_call (current_class_ref, ansi_assopname (NOP_EXPR), - build_tree_list (NULL_TREE, - converted_parm), + &parmvec, base_binfo, - LOOKUP_NORMAL | LOOKUP_NONVIRTUAL)); + LOOKUP_NORMAL | LOOKUP_NONVIRTUAL, + tf_warning_or_error)); + release_tree_vector (parmvec); } /* Assign to each of the non-static data members. */ for (fields = TYPE_FIELDS (current_class_type); fields; - fields = TREE_CHAIN (fields)) + fields = DECL_CHAIN (fields)) { tree comp = current_class_ref; tree init = parm; @@ -714,7 +664,8 @@ do_build_assign_ref (tree fndecl) else if (ANON_AGGR_TYPE_P (expr_type) && TYPE_FIELDS (expr_type) != NULL_TREE) /* Just use the field; anonymous types can't have - nontrivial copy ctors or assignment ops. */; + nontrivial copy ctors or assignment ops or this + function would be deleted. */; else continue; @@ -727,9 +678,12 @@ do_build_assign_ref (tree fndecl) expr_type = cp_build_qualified_type (expr_type, quals); init = build3 (COMPONENT_REF, expr_type, init, field, NULL_TREE); + if (move_p && TREE_CODE (expr_type) != REFERENCE_TYPE) + init = move (init); if (DECL_NAME (field)) - init = build_modify_expr (comp, NOP_EXPR, init); + init = cp_build_modify_expr (comp, NOP_EXPR, init, + tf_warning_or_error); else init = build2 (MODIFY_EXPR, TREE_TYPE (comp), comp, init); finish_expr_stmt (init); @@ -770,7 +724,7 @@ synthesize_method (tree fndecl) if (! context) push_to_top_level (); else if (nested) - push_function_context_to (context); + push_function_context (); input_location = DECL_SOURCE_LOCATION (fndecl); @@ -779,7 +733,7 @@ synthesize_method (tree fndecl) if (DECL_OVERLOADED_OPERATOR_P (fndecl) == NOP_EXPR) { - do_build_assign_ref (fndecl); + do_build_copy_assign (fndecl); need_body = false; } else if (DECL_CONSTRUCTOR_P (fndecl)) @@ -808,170 +762,545 @@ synthesize_method (tree fndecl) if (! context) pop_from_top_level (); else if (nested) - pop_function_context_from (context); + pop_function_context (); pop_deferring_access_checks (); if (error_count != errorcount || warning_count != warningcount) - inform ("%Hsynthesized method %qD first required here ", - &input_location, fndecl); + inform (input_location, "synthesized method %qD first required here ", + fndecl); } -/* Use EXTRACTOR to locate the relevant function called for each base & - class field of TYPE. CLIENT allows additional information to be passed - to EXTRACTOR. Generates the union of all exceptions generated by those - functions. Note that we haven't updated TYPE_FIELDS and such of any - variants yet, so we need to look at the main one. */ +/* Build a reference to type TYPE with cv-quals QUALS, which is an + rvalue if RVALUE is true. */ static tree -synthesize_exception_spec (tree type, tree (*extractor) (tree, void*), - void *client) +build_stub_type (tree type, int quals, bool rvalue) { - tree raises = empty_except_spec; - tree fields = TYPE_FIELDS (type); - tree binfo, base_binfo; - int i; + tree argtype = cp_build_qualified_type (type, quals); + return cp_build_reference_type (argtype, rvalue); +} - for (binfo = TYPE_BINFO (type), i = 0; - BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) - { - tree fn = (*extractor) (BINFO_TYPE (base_binfo), client); - if (fn) - { - tree fn_raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)); +/* Build a dummy glvalue from dereferencing a dummy reference of type + REFTYPE. */ - raises = merge_exception_specifiers (raises, fn_raises); - } - } - for (; fields; fields = TREE_CHAIN (fields)) - { - tree type = TREE_TYPE (fields); - tree fn; +static tree +build_stub_object (tree reftype) +{ + tree stub = build1 (NOP_EXPR, reftype, integer_one_node); + return convert_from_reference (stub); +} - if (TREE_CODE (fields) != FIELD_DECL || DECL_ARTIFICIAL (fields)) - continue; - while (TREE_CODE (type) == ARRAY_TYPE) - type = TREE_TYPE (type); - if (!CLASS_TYPE_P (type)) - continue; +/* Determine which function will be called when looking up NAME in TYPE, + called with a single ARGTYPE argument, or no argument if ARGTYPE is + null. FLAGS and COMPLAIN are as for build_new_method_call. - fn = (*extractor) (type, client); - if (fn) - { - tree fn_raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)); + Returns a FUNCTION_DECL if all is well. + Returns NULL_TREE if overload resolution failed. + Returns error_mark_node if the chosen function cannot be called. */ - raises = merge_exception_specifiers (raises, fn_raises); - } +static tree +locate_fn_flags (tree type, tree name, tree argtype, int flags, + tsubst_flags_t complain) +{ + tree ob, fn, fns, binfo, rval; + VEC(tree,gc) *args; + + if (TYPE_P (type)) + binfo = TYPE_BINFO (type); + else + { + binfo = type; + type = BINFO_TYPE (binfo); } - return raises; + + ob = build_stub_object (cp_build_reference_type (type, false)); + args = make_tree_vector (); + if (argtype) + { + tree arg = build_stub_object (argtype); + VEC_quick_push (tree, args, arg); + } + + fns = lookup_fnfields (binfo, name, 0); + rval = build_new_method_call (ob, fns, &args, binfo, flags, &fn, complain); + + release_tree_vector (args); + if (fn && rval == error_mark_node) + return rval; + else + return fn; } /* Locate the dtor of TYPE. */ tree -locate_dtor (tree type, void *client ATTRIBUTE_UNUSED) +get_dtor (tree type) { - return CLASSTYPE_DESTRUCTORS (type); + tree fn = locate_fn_flags (type, complete_dtor_identifier, NULL_TREE, + LOOKUP_NORMAL, tf_warning_or_error); + if (fn == error_mark_node) + return NULL_TREE; + return fn; } /* Locate the default ctor of TYPE. */ tree -locate_ctor (tree type, void *client ATTRIBUTE_UNUSED) +locate_ctor (tree type) +{ + tree fn = locate_fn_flags (type, complete_ctor_identifier, NULL_TREE, + LOOKUP_SPECULATIVE, tf_none); + if (fn == error_mark_node) + return NULL_TREE; + return fn; +} + +/* Likewise, but give any appropriate errors. */ + +tree +get_default_ctor (tree type) { - tree fns; + tree fn = locate_fn_flags (type, complete_ctor_identifier, NULL_TREE, + LOOKUP_NORMAL, tf_warning_or_error); + if (fn == error_mark_node) + return NULL_TREE; + return fn; +} + +/* Locate the copy ctor of TYPE. */ + +tree +get_copy_ctor (tree type) +{ + int quals = (TYPE_HAS_CONST_COPY_CTOR (type) + ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED); + tree argtype = build_stub_type (type, quals, false); + tree fn = locate_fn_flags (type, complete_ctor_identifier, argtype, + LOOKUP_NORMAL, tf_warning_or_error); + if (fn == error_mark_node) + return NULL_TREE; + return fn; +} + +/* Locate the copy assignment operator of TYPE. */ - if (!TYPE_HAS_DEFAULT_CONSTRUCTOR (type)) +tree +get_copy_assign (tree type) +{ + int quals = (TYPE_HAS_CONST_COPY_ASSIGN (type) + ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED); + tree argtype = build_stub_type (type, quals, false); + tree fn = locate_fn_flags (type, ansi_assopname (NOP_EXPR), argtype, + LOOKUP_NORMAL, tf_warning_or_error); + if (fn == error_mark_node) return NULL_TREE; + return fn; +} + +/* Subroutine of synthesized_method_walk. Update SPEC_P, TRIVIAL_P and + DELETED_P or give an error message MSG with argument ARG. */ - /* Call lookup_fnfields_1 to create the constructor declarations, if - necessary. */ - if (CLASSTYPE_LAZY_DEFAULT_CTOR (type)) - return lazily_declare_fn (sfk_constructor, type); +static void +process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p, + bool *deleted_p, const char *msg, tree arg) +{ + if (!fn || fn == error_mark_node) + goto bad; - for (fns = CLASSTYPE_CONSTRUCTORS (type); fns; fns = OVL_NEXT (fns)) + if (spec_p) { - tree fn = OVL_CURRENT (fns); - tree parms = TYPE_ARG_TYPES (TREE_TYPE (fn)); + tree raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)); + *spec_p = merge_exception_specifiers (*spec_p, raises); + } - parms = skip_artificial_parms_for (fn, parms); + if (!trivial_fn_p (fn)) + { + if (trivial_p) + *trivial_p = false; + if (TREE_CODE (arg) == FIELD_DECL + && TREE_CODE (DECL_CONTEXT (arg)) == UNION_TYPE) + { + if (deleted_p) + *deleted_p = true; + if (msg) + error ("union member %q+D with non-trivial %qD", arg, fn); + } + } - if (sufficient_parms_p (parms)) - return fn; + if (move_p && !move_fn_p (fn) && !trivial_fn_p (fn)) + { + if (msg) + error (msg, arg); + goto bad; } - gcc_unreachable (); + + return; + + bad: + if (deleted_p) + *deleted_p = true; } -struct copy_data +/* Subroutine of synthesized_method_walk to allow recursion into anonymous + aggregates. */ + +static void +walk_field_subobs (tree fields, tree fnname, special_function_kind sfk, + int quals, bool copy_arg_p, bool move_p, + bool assign_p, tree *spec_p, bool *trivial_p, + bool *deleted_p, const char *msg, + int flags, tsubst_flags_t complain) { - tree name; - int quals; -}; + tree field; + for (field = fields; field; field = DECL_CHAIN (field)) + { + tree mem_type, argtype, rval; -/* Locate the copy ctor or copy assignment of TYPE. CLIENT_ - points to a COPY_DATA holding the name (NULL for the ctor) - and desired qualifiers of the source operand. */ + if (TREE_CODE (field) != FIELD_DECL + || DECL_ARTIFICIAL (field)) + continue; -tree -locate_copy (tree type, void *client_) + mem_type = strip_array_types (TREE_TYPE (field)); + if (assign_p) + { + bool bad = true; + if (CP_TYPE_CONST_P (mem_type) && !CLASS_TYPE_P (mem_type)) + { + if (msg) + error ("non-static const member %q#D, can't use default " + "assignment operator", field); + } + else if (TREE_CODE (mem_type) == REFERENCE_TYPE) + { + if (msg) + error ("non-static reference member %q#D, can't use " + "default assignment operator", field); + } + else + bad = false; + + if (bad && deleted_p) + *deleted_p = true; + } + else if (sfk == sfk_constructor) + { + bool bad = true; + if (CP_TYPE_CONST_P (mem_type) + && (!CLASS_TYPE_P (mem_type) + || !type_has_user_provided_default_constructor (mem_type))) + { + if (msg) + error ("uninitialized non-static const member %q#D", + field); + } + else if (TREE_CODE (mem_type) == REFERENCE_TYPE) + { + if (msg) + error ("uninitialized non-static reference member %q#D", + field); + } + else + bad = false; + + if (bad && deleted_p) + *deleted_p = true; + } + + if (!CLASS_TYPE_P (mem_type)) + continue; + + if (ANON_AGGR_TYPE_P (mem_type)) + { + walk_field_subobs (TYPE_FIELDS (mem_type), fnname, sfk, quals, + copy_arg_p, move_p, assign_p, spec_p, trivial_p, + deleted_p, msg, flags, complain); + continue; + } + + if (copy_arg_p) + { + int mem_quals = cp_type_quals (mem_type) | quals; + if (DECL_MUTABLE_P (field)) + mem_quals &= ~TYPE_QUAL_CONST; + argtype = build_stub_type (mem_type, mem_quals, move_p); + } + else + argtype = NULL_TREE; + + rval = locate_fn_flags (mem_type, fnname, argtype, flags, complain); + + process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p, + msg, field); + } +} + +/* The caller wants to generate an implicit declaration of SFK for CTYPE + which is const if relevant and CONST_P is set. If spec_p, trivial_p and + deleted_p are non-null, set their referent appropriately. If diag is + true, we're being called from maybe_explain_implicit_delete to give + errors. */ + +static void +synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, + tree *spec_p, bool *trivial_p, bool *deleted_p, + bool diag) { - struct copy_data *client = (struct copy_data *)client_; - tree fns; - tree best = NULL_TREE; - bool excess_p = false; + tree binfo, base_binfo, scope, fnname, rval, argtype; + bool move_p, copy_arg_p, assign_p, expected_trivial, check_vdtor; + VEC(tree,gc) *vbases; + int i, quals, flags; + tsubst_flags_t complain; + const char *msg; + + if (spec_p) + *spec_p = (cxx_dialect >= cxx0x + ? noexcept_true_spec : empty_except_spec); + + if (deleted_p) + { + /* "The closure type associated with a lambda-expression has a deleted + default constructor and a deleted copy assignment operator." + This is diagnosed in maybe_explain_implicit_delete. */ + if (LAMBDA_TYPE_P (ctype) + && (sfk == sfk_constructor + || sfk == sfk_copy_assignment)) + { + *deleted_p = true; + return; + } + + *deleted_p = false; + } + + move_p = false; + switch (sfk) + { + case sfk_constructor: + case sfk_destructor: + copy_arg_p = false; + break; + + case sfk_move_constructor: + case sfk_move_assignment: + move_p = true; + case sfk_copy_constructor: + case sfk_copy_assignment: + copy_arg_p = true; + break; + + default: + gcc_unreachable (); + } + + expected_trivial = type_has_trivial_fn (ctype, sfk); + if (trivial_p) + *trivial_p = expected_trivial; + +#ifndef ENABLE_CHECKING + /* The TYPE_HAS_COMPLEX_* flags tell us about constraints from base + class versions and other properties of the type. But a subobject + class can be trivially copyable and yet have overload resolution + choose a template constructor for initialization, depending on + rvalueness and cv-quals. So we can't exit early for copy/move + methods in C++0x. */ + if (expected_trivial + && (!copy_arg_p || cxx_dialect < cxx0x)) + return; +#endif - if (client->name) + assign_p = false; + check_vdtor = false; + switch (sfk) { - int ix; - ix = lookup_fnfields_1 (type, client->name); - if (ix < 0) - return NULL_TREE; - fns = VEC_index (tree, CLASSTYPE_METHOD_VEC (type), ix); + case sfk_move_assignment: + case sfk_copy_assignment: + assign_p = true; + fnname = ansi_assopname (NOP_EXPR); + break; + + case sfk_destructor: + check_vdtor = true; + /* The synthesized method will call base dtors, but check complete + here to avoid having to deal with VTT. */ + fnname = complete_dtor_identifier; + break; + + case sfk_constructor: + case sfk_move_constructor: + case sfk_copy_constructor: + fnname = complete_ctor_identifier; + break; + + default: + gcc_unreachable (); } - else if (TYPE_HAS_INIT_REF (type)) + + ++cp_unevaluated_operand; + ++c_inhibit_evaluation_warnings; + + scope = push_scope (ctype); + + if (diag) { - /* If construction of the copy constructor was postponed, create - it now. */ - if (CLASSTYPE_LAZY_COPY_CTOR (type)) - lazily_declare_fn (sfk_copy_constructor, type); - fns = CLASSTYPE_CONSTRUCTORS (type); + flags = LOOKUP_NORMAL|LOOKUP_SPECULATIVE; + complain = tf_warning_or_error; } else - return NULL_TREE; - for (; fns; fns = OVL_NEXT (fns)) { - tree fn = OVL_CURRENT (fns); - tree parms = TYPE_ARG_TYPES (TREE_TYPE (fn)); - tree src_type; - int excess; - int quals; - - parms = skip_artificial_parms_for (fn, parms); - if (!parms) - continue; - src_type = non_reference (TREE_VALUE (parms)); + flags = LOOKUP_PROTECT|LOOKUP_SPECULATIVE; + complain = tf_none; + } - if (src_type == error_mark_node) - return NULL_TREE; + if (const_p) + quals = TYPE_QUAL_CONST; + else + quals = TYPE_UNQUALIFIED; + argtype = NULL_TREE; + + if (!diag) + msg = NULL; + else if (assign_p) + msg = ("base %qT does not have a move assignment operator or trivial " + "copy assignment operator"); + else + msg = ("base %qT does not have a move constructor or trivial " + "copy constructor"); - if (!same_type_ignoring_top_level_qualifiers_p (src_type, type)) - continue; - if (!sufficient_parms_p (TREE_CHAIN (parms))) - continue; - quals = cp_type_quals (src_type); - if (client->quals & ~quals) - continue; - excess = quals & ~client->quals; - if (!best || (excess_p && !excess)) + for (binfo = TYPE_BINFO (ctype), i = 0; + BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) + { + tree basetype = BINFO_TYPE (base_binfo); + if (copy_arg_p) + argtype = build_stub_type (basetype, quals, move_p); + rval = locate_fn_flags (base_binfo, fnname, argtype, flags, complain); + + process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p, + msg, BINFO_TYPE (base_binfo)); + + if (check_vdtor && type_has_virtual_destructor (basetype)) { - best = fn; - excess_p = excess; + rval = locate_fn_flags (ctype, ansi_opname (DELETE_EXPR), + ptr_type_node, flags, complain); + /* Unlike for base ctor/op=/dtor, for operator delete it's fine + to have a null rval (no class-specific op delete). */ + if (rval && rval == error_mark_node && deleted_p) + *deleted_p = true; + } + } + + vbases = CLASSTYPE_VBASECLASSES (ctype); + if (vbases && assign_p && move_p) + { + /* Should the spec be changed to allow vbases that only occur once? */ + if (diag) + error ("%qT has virtual bases, default move assignment operator " + "cannot be generated", ctype); + else if (deleted_p) + *deleted_p = true; + } + else if (!assign_p) + { + if (diag) + msg = ("virtual base %qT does not have a move constructor " + "or trivial copy constructor"); + FOR_EACH_VEC_ELT (tree, vbases, i, base_binfo) + { + if (copy_arg_p) + argtype = build_stub_type (BINFO_TYPE (base_binfo), quals, move_p); + rval = locate_fn_flags (base_binfo, fnname, argtype, flags, complain); + + process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p, + msg, BINFO_TYPE (base_binfo)); } - else - /* Ambiguous */ - return NULL_TREE; } - return best; + if (!diag) + /* Leave msg null. */; + else if (assign_p) + msg = ("non-static data member %qD does not have a move " + "assignment operator or trivial copy assignment operator"); + else + msg = ("non-static data member %qD does not have a move " + "constructor or trivial copy constructor"); + walk_field_subobs (TYPE_FIELDS (ctype), fnname, sfk, quals, + copy_arg_p, move_p, assign_p, spec_p, trivial_p, + deleted_p, msg, flags, complain); + + pop_scope (scope); + + --cp_unevaluated_operand; + --c_inhibit_evaluation_warnings; + +#ifdef ENABLE_CHECKING + /* If we expected this to be trivial but it isn't, then either we're in + C++0x mode and this is a copy/move ctor/op= or there's an error. */ + gcc_assert (!(trivial_p && expected_trivial && !*trivial_p) + || (copy_arg_p && cxx_dialect >= cxx0x) + || errorcount); +#endif +} + +/* DECL is a deleted function. If it's implicitly deleted, explain why and + return true; else return false. */ + +bool +maybe_explain_implicit_delete (tree decl) +{ + /* If decl is a clone, get the primary variant. */ + decl = DECL_ORIGIN (decl); + gcc_assert (DECL_DELETED_FN (decl)); + if (DECL_DEFAULTED_FN (decl) + && DECL_INITIAL (decl) == NULL_TREE) + { + /* Not marked GTY; it doesn't need to be GC'd or written to PCH. */ + static htab_t explained_htab; + void **slot; + + special_function_kind sfk; + location_t loc; + bool informed; + tree ctype; + + if (!explained_htab) + explained_htab = htab_create (37, htab_hash_pointer, + htab_eq_pointer, NULL); + slot = htab_find_slot (explained_htab, decl, INSERT); + if (*slot) + return true; + *slot = decl; + + sfk = special_function_p (decl); + ctype = DECL_CONTEXT (decl); + loc = input_location; + input_location = DECL_SOURCE_LOCATION (decl); + + informed = false; + if (LAMBDA_TYPE_P (ctype)) + { + informed = true; + if (sfk == sfk_constructor) + error ("a lambda closure type has a deleted default constructor"); + else if (sfk == sfk_copy_assignment) + error ("a lambda closure type has a deleted copy assignment operator"); + else + informed = false; + } + if (!informed) + { + tree parm_type = TREE_VALUE (FUNCTION_FIRST_USER_PARMTYPE (decl)); + bool const_p = CP_TYPE_CONST_P (non_reference (parm_type)); + tree scope = push_scope (ctype); + error ("%qD is implicitly deleted because the default " + "definition would be ill-formed:", decl); + pop_scope (scope); + synthesized_method_walk (ctype, sfk, const_p, + NULL, NULL, NULL, true); + } + + input_location = loc; + return true; + } + return false; } /* Implicitly declare the special function indicated by KIND, as a @@ -992,6 +1321,8 @@ implicitly_declare_fn (special_function_kind kind, tree type, bool const_p) tree this_parm; tree name; HOST_WIDE_INT saved_processing_template_decl; + bool deleted_p; + bool trivial_p; /* Because we create declarations for implicitly declared functions lazily, we may be creating the declaration for a member of TYPE @@ -1023,54 +1354,57 @@ implicitly_declare_fn (special_function_kind kind, tree type, bool const_p) case sfk_destructor: /* Destructor. */ name = constructor_name (type); - raises = synthesize_exception_spec (type, &locate_dtor, 0); break; case sfk_constructor: /* Default constructor. */ name = constructor_name (type); - raises = synthesize_exception_spec (type, &locate_ctor, 0); break; case sfk_copy_constructor: - case sfk_assignment_operator: + case sfk_copy_assignment: + case sfk_move_constructor: + case sfk_move_assignment: { - struct copy_data data; - - data.name = NULL; - data.quals = 0; - if (kind == sfk_assignment_operator) + bool move_p; + if (kind == sfk_copy_assignment + || kind == sfk_move_assignment) { return_type = build_reference_type (type); name = ansi_assopname (NOP_EXPR); - data.name = name; } else name = constructor_name (type); 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); + move_p = (kind == sfk_move_assignment + || kind == sfk_move_constructor); + rhs_parm_type = cp_build_reference_type (rhs_parm_type, move_p); + parameter_types = tree_cons (NULL_TREE, rhs_parm_type, parameter_types); - raises = synthesize_exception_spec (type, &locate_copy, &data); break; } default: gcc_unreachable (); } + synthesized_method_walk (type, kind, const_p, &raises, &trivial_p, + &deleted_p, false); + + if (!trivial_p && type_has_trivial_fn (type, kind)) + type_set_nontrivial_flag (type, kind); + /* Create the function. */ fn_type = build_method_type_directly (type, return_type, parameter_types); if (raises) 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; @@ -1098,7 +1432,7 @@ implicitly_declare_fn (special_function_kind kind, tree type, bool const_p) } /* Add the "this" parameter. */ this_parm = build_this_parm (fn_type, TYPE_UNQUALIFIED); - TREE_CHAIN (this_parm) = DECL_ARGUMENTS (fn); + DECL_CHAIN (this_parm) = DECL_ARGUMENTS (fn); DECL_ARGUMENTS (fn) = this_parm; grokclassfn (type, fn, kind == sfk_destructor ? DTOR_FLAG : NO_SPECIAL); @@ -1106,9 +1440,11 @@ implicitly_declare_fn (special_function_kind kind, tree type, bool const_p) rest_of_decl_compilation (fn, toplevel_bindings_p (), at_eof); DECL_IN_AGGR_P (fn) = 1; DECL_ARTIFICIAL (fn) = 1; + DECL_DEFAULTED_FN (fn) = 1; + if (cxx_dialect >= cxx0x) + DECL_DELETED_FN (fn) = deleted_p; DECL_NOT_REALLY_EXTERN (fn) = 1; DECL_DECLARED_INLINE_P (fn) = 1; - DECL_INLINE (fn) = 1; gcc_assert (!TREE_USED (fn)); /* Restore PROCESSING_TEMPLATE_DECL. */ @@ -1117,6 +1453,107 @@ implicitly_declare_fn (special_function_kind kind, tree type, bool const_p) 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); + } + + /* 8.4.2/2: If it is explicitly defaulted on its first declaration, it is + implicitly considered to have the same exception-specification as if + it had been implicitly declared. */ + if (DECL_DEFAULTED_IN_CLASS_P (fn)) + { + tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn)); + TREE_TYPE (fn) = build_exception_variant (TREE_TYPE (fn), eh_spec); + } + + if (DECL_DELETED_FN (implicit_fn)) + DECL_DELETED_FN (fn) = 1; +} + +/* 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) + { + if (copy_fn_p (fn)) + kind = sfk_copy_assignment; + else if (move_fn_p (fn)) + kind = sfk_move_assignment; + } + + 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. */ @@ -1125,21 +1562,49 @@ tree lazily_declare_fn (special_function_kind sfk, tree type) { tree fn; - bool const_p; - - /* Figure out whether or not the argument has a const reference - type. */ - if (sfk == sfk_copy_constructor) - const_p = TYPE_HAS_CONST_INIT_REF (type); - else if (sfk == sfk_assignment_operator) - const_p = TYPE_HAS_CONST_ASSIGN_REF (type); - else - /* In this case, CONST_P will be ignored. */ - const_p = false; + /* Whether or not the argument has a const reference type. */ + bool const_p = false; + + switch (sfk) + { + case sfk_constructor: + CLASSTYPE_LAZY_DEFAULT_CTOR (type) = 0; + break; + case sfk_copy_constructor: + const_p = TYPE_HAS_CONST_COPY_CTOR (type); + CLASSTYPE_LAZY_COPY_CTOR (type) = 0; + break; + case sfk_move_constructor: + CLASSTYPE_LAZY_MOVE_CTOR (type) = 0; + break; + case sfk_copy_assignment: + const_p = TYPE_HAS_CONST_COPY_ASSIGN (type); + CLASSTYPE_LAZY_COPY_ASSIGN (type) = 0; + break; + case sfk_move_assignment: + CLASSTYPE_LAZY_MOVE_ASSIGN (type) = 0; + break; + case sfk_destructor: + CLASSTYPE_LAZY_DESTRUCTOR (type) = 0; + break; + default: + gcc_unreachable (); + } + /* Declare the function. */ fn = implicitly_declare_fn (sfk, type, const_p); + + /* For move variants, rather than declare them as deleted we just + don't declare them at all. */ + if (DECL_DELETED_FN (fn) + && (sfk == sfk_move_constructor + || sfk == sfk_move_assignment)) + return NULL_TREE; + /* A destructor may be virtual. */ - if (sfk == sfk_destructor) + if (sfk == sfk_destructor + || sfk == sfk_move_assignment + || sfk == sfk_copy_assignment) check_for_override (fn, type); /* Add it to CLASSTYPE_METHOD_VEC. */ add_method (type, fn, NULL_TREE); @@ -1155,29 +1620,19 @@ lazily_declare_fn (special_function_kind sfk, tree type) /* 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", type); - TREE_CHAIN (fn) = TYPE_METHODS (type); + DECL_CHAIN (fn) = TYPE_METHODS (type); TYPE_METHODS (type) = fn; } maybe_add_class_template_decl_list (type, fn, /*friend_p=*/0); - if (sfk == sfk_assignment_operator) - CLASSTYPE_LAZY_ASSIGNMENT_OP (type) = 0; - else - { - /* Remember that the function has been created. */ - if (sfk == sfk_constructor) - CLASSTYPE_LAZY_DEFAULT_CTOR (type) = 0; - else if (sfk == sfk_copy_constructor) - CLASSTYPE_LAZY_COPY_CTOR (type) = 0; - else if (sfk == sfk_destructor) - CLASSTYPE_LAZY_DESTRUCTOR (type) = 0; - /* Create appropriate clones. */ - clone_function_decl (fn, /*update_method_vec=*/true); - } + if (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (fn) + || DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (fn)) + /* Create appropriate clones. */ + clone_function_decl (fn, /*update_method_vec=*/true); return fn; }