Index: gcc/doc/invoke.texi =================================================================== --- gcc/doc/invoke.texi (revision 162162) +++ gcc/doc/invoke.texi (working copy) @@ -192,9 +192,9 @@ -fno-optional-diags -fpermissive @gol -fno-pretty-templates @gol -frepo -fno-rtti -fstats -ftemplate-depth=@var{n} @gol --fno-threadsafe-statics -fuse-cxa-atexit -fno-weak -nostdinc++ @gol --fno-default-inline -fvisibility-inlines-hidden @gol --fvisibility-ms-compat @gol +-fno-threadsafe-statics -fuse-cxa-atexit -fuse-key-methods @gol +-fno-weak -nostdinc++ -fno-default-inline @gol +-fvisibility-inlines-hidden -fvisibility-ms-compat @gol -Wabi -Wconversion-null -Wctor-dtor-privacy @gol -Wnoexcept -Wnon-virtual-dtor -Wreorder @gol -Weffc++ -Wstrict-null-sentinel @gol @@ -2043,6 +2043,12 @@ will cause @code{std::uncaught_exception} to be incorrect, but is necessary if the runtime routine is not available. +@item -fuse-key-methods +@opindex fuse-key-methods +Emit an artificial use of any key method whose class's vtable or +typeinfo is used, to help the linker emit a better diagnostic if the +key method is never defined. See also @xref{Vague Linkage}. + @item -fvisibility-inlines-hidden @opindex fvisibility-inlines-hidden This switch declares that the user does not attempt to compare Index: gcc/c-family/c.opt =================================================================== --- gcc/c-family/c.opt (revision 162162) +++ gcc/c-family/c.opt (working copy) @@ -833,6 +833,12 @@ C++ ObjC++ Var(flag_use_cxa_get_exception_ptr) Init(2) Use __cxa_get_exception_ptr in exception handling +fuse-key-methods +C++ Report Var(flag_use_key_methods) +Emit an artificial use of any key method whose class's vtable or +typeinfo is used, to help the linker emit a better diagnostic if the +key method is never defined. + fvisibility-inlines-hidden C++ ObjC++ Marks all inlined methods as having hidden visibility Index: gcc/testsuite/g++.dg/diagnostic/keymethod1.C =================================================================== --- gcc/testsuite/g++.dg/diagnostic/keymethod1.C (revision 0) +++ gcc/testsuite/g++.dg/diagnostic/keymethod1.C (revision 0) @@ -0,0 +1,71 @@ +// Make sure uses of a class's vtable or typeinfo cause the assembly +// to include a reference to the key method, so that the linker +// produces a readable error. +// { dg-options "-O2 -fuse-key-methods" } + +#include + +struct TypeinfoUsed { +// { dg-final { scan-assembler _ZN12TypeinfoUsedD1Ev } } + virtual ~TypeinfoUsed(); +}; + +const std::type_info& use1() { + return typeid(TypeinfoUsed); +} + +struct VTableUsed { +// { dg-final { scan-assembler _ZN10VTableUsed9KeyMethodEv } } + virtual void KeyMethod(); + virtual ~VTableUsed(); +}; + +void use2() { + VTableUsed vtu; +} + +struct UnUsed { +// { dg-final { scan-assembler-not _ZN6UnUsedD1Ev } } + virtual ~UnUsed(); + + void Method(); +}; + +void use2(UnUsed* uu) { + uu->Method(); +} + +struct NoKeyMethod { +// { dg-final { scan-assembler-not _ZN11NoKeyMethodD1Ev } } + inline virtual ~NoKeyMethod(); +}; +NoKeyMethod::~NoKeyMethod() {} + +const std::type_info& use3() { + return typeid(NoKeyMethod); +} + +struct PrivateKeyMethod { +private: +// { dg-final { scan-assembler _ZN16PrivateKeyMethod9KeyMethodEv } } + virtual void KeyMethod(); +public: + virtual ~PrivateKeyMethod(); +}; + +void use4() { + PrivateKeyMethod pkm; +} + +struct InlineKeyMethod { +// { dg-final { scan-assembler _ZN15InlineKeyMethod9KeyMethodEv { target { ! arm*-*-*eabi* } } } } + virtual void KeyMethod(); +// { dg-final { scan-assembler _ZN15InlineKeyMethodD1Ev { target arm*-*-*eabi* } } } + virtual ~InlineKeyMethod(); +}; + +inline void InlineKeyMethod::KeyMethod() {} + +const std::type_info& use5() { + return typeid(InlineKeyMethod); +} Index: gcc/cp/typeck.c =================================================================== --- gcc/cp/typeck.c (revision 162162) +++ gcc/cp/typeck.c (working copy) @@ -6011,7 +6011,7 @@ converted expression. */ tree -convert_member_func_to_ptr (tree type, tree expr) +convert_member_func_to_ptr (tree type, tree expr, tsubst_flags_t complain) { tree intype; tree decl; @@ -6020,7 +6020,7 @@ gcc_assert (TYPE_PTRMEMFUNC_P (intype) || TREE_CODE (intype) == METHOD_TYPE); - if (pedantic || warn_pmf2ptr) + if ((complain & tf_warning) && (pedantic || warn_pmf2ptr)) pedwarn (input_location, pedantic ? OPT_pedantic : OPT_Wpmf_conversions, "converting from %qT to %qT", intype, type); @@ -6112,7 +6112,7 @@ && TYPE_PTR_P (type) && (TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE || VOID_TYPE_P (TREE_TYPE (type)))) - return convert_member_func_to_ptr (type, expr); + return convert_member_func_to_ptr (type, expr, complain); /* If the cast is not to a reference type, the lvalue-to-rvalue, array-to-pointer, and function-to-pointer conversions are Index: gcc/cp/decl2.c =================================================================== --- gcc/cp/decl2.c (revision 162162) +++ gcc/cp/decl2.c (working copy) @@ -100,6 +100,12 @@ sure are defined. */ static GTY(()) VEC(tree,gc) *no_linkage_decls; +/* A list of key methods to mark as used. The currrent translation + unit uses symbols with vague linkage that will be emitted with + these key methods, so we want the linker to mention the key methods + in addition to the vague symbols if they're missing. */ +static GTY(()) VEC(tree,gc) *needed_key_methods; + /* Nonzero if we're done parsing and into end-of-file activities. */ int at_eof; @@ -1682,6 +1688,7 @@ { /* -1 for imported, 1 for exported. */ int import_export = 0; + tree key_method = NULL_TREE; /* It only makes sense to call this function at EOF. The reason is that this function looks at whether or not the first non-inline @@ -1716,15 +1723,15 @@ { /* The ABI specifies that the virtual table and associated information are emitted with the key method, if any. */ - tree method = CLASSTYPE_KEY_METHOD (ctype); + key_method = CLASSTYPE_KEY_METHOD (ctype); /* If weak symbol support is not available, then we must be careful not to emit the vtable when the key function is inline. An inline function can be defined in multiple translation units. If we were to emit the vtable in each translation unit containing a definition, we would get multiple definition errors at link-time. */ - if (method && (flag_weak || ! DECL_DECLARED_INLINE_P (method))) - import_export = (DECL_REALLY_EXTERN (method) ? -1 : 1); + if (key_method && (flag_weak || ! DECL_DECLARED_INLINE_P (key_method))) + import_export = (DECL_REALLY_EXTERN (key_method) ? -1 : 1); } /* When MULTIPLE_SYMBOL_SPACES is set, we cannot count on seeing @@ -1740,6 +1747,8 @@ { SET_CLASSTYPE_INTERFACE_KNOWN (ctype); CLASSTYPE_INTERFACE_ONLY (ctype) = (import_export < 0); + if (import_export < 0 && key_method) + VEC_safe_push (tree, gc, needed_key_methods, key_method); } } @@ -3346,6 +3355,75 @@ return 0; } +/* Takes the list of functions in needed_key_methods, and "uses" them. + At the moment, we do this by emitting a static function that calls + each of them. The linker's GC will remove this again since it's + unused. */ +static void +emit_needed_key_methods (void) +{ + tree body, fndecl, fn, key_method, voidfnty; + unsigned i; + + /* Filter out key methods for which this file doesn't use their + class's vtable or typeinfo. XXX: Any others? */ + for (i = 0; VEC_iterate (tree, needed_key_methods, i, key_method);) + { + bool needed = false; + tree vtbl; + const tree type = DECL_CONTEXT (key_method); + + if (TREE_USED (get_tinfo_decl (type))) + needed = true; + for (vtbl = CLASSTYPE_VTABLES (type); vtbl && !needed; + vtbl = TREE_CHAIN (vtbl)) + { + if (TREE_USED (vtbl)) + needed = true; + } + + if (needed) + ++i; + else + VEC_unordered_remove (tree, needed_key_methods, i); + } + + /* Don't build a function if we don't depend on any key methods. */ + if (VEC_length (tree, needed_key_methods) == 0) + return; + + voidfnty = build_function_type_list (void_type_node, NULL_TREE); + fndecl = build_lang_decl (FUNCTION_DECL, + get_identifier ("__use_needed_key_methods"), + voidfnty); + start_preparsed_function (fndecl, NULL_TREE, SF_PRE_PARSED); + TREE_PUBLIC (fndecl) = 0; + /* Mark as artificial because it's not explicitly in the user's + source code. */ + DECL_ARTIFICIAL (fndecl) = 1; + /* Attach attribute((used)) to the declaration so the optimizers + don't delete it. */ + DECL_PRESERVE_P (fndecl) = 1; + + body = begin_compound_stmt (BCS_FN_BODY); + + for (i = 0; VEC_iterate (tree, needed_key_methods, i, key_method); ++i) + { + /* "Use" the key method by casting it to void(*)() and calling that. */ + tree casted_key_method = + cp_build_c_cast (build_pointer_type (voidfnty), + key_method, tf_error); + tree call = cp_build_function_call_nary (casted_key_method, tf_error, + NULL_TREE); + finish_expr_stmt (call); + } + + finish_compound_stmt (body); + fn = finish_function (0); + + expand_or_defer_fn (fn); +} + /* Called via LANGHOOK_CALLGRAPH_ANALYZE_EXPR. It is supposed to mark decls referenced from front-end specific constructs; it will be called only for language-specific tree nodes. @@ -3918,6 +3996,10 @@ linkage now. */ pop_lang_context (); + /* Use all the needed key methods. */ + if (flag_use_key_methods) + emit_needed_key_methods (); + /* Collect candidates for Java hidden aliases. */ candidates = collect_candidates_for_java_method_aliases (); Index: gcc/cp/cvt.c =================================================================== --- gcc/cp/cvt.c (revision 162162) +++ gcc/cp/cvt.c (working copy) @@ -110,7 +110,7 @@ { if (TYPE_PTRMEMFUNC_P (intype) || TREE_CODE (intype) == METHOD_TYPE) - return convert_member_func_to_ptr (type, expr); + return convert_member_func_to_ptr (type, expr, tf_warning_or_error); if (TREE_CODE (TREE_TYPE (expr)) == POINTER_TYPE) return build_nop (type, expr); intype = TREE_TYPE (expr); Index: gcc/cp/cp-tree.h =================================================================== --- gcc/cp/cp-tree.h (revision 162162) +++ gcc/cp/cp-tree.h (working copy) @@ -5522,7 +5522,7 @@ extern tree non_reference (tree); extern tree lookup_anon_field (tree, tree); extern bool invalid_nonstatic_memfn_p (const_tree, tsubst_flags_t); -extern tree convert_member_func_to_ptr (tree, tree); +extern tree convert_member_func_to_ptr (tree, tree, tsubst_flags_t); extern tree convert_ptrmem (tree, tree, bool, bool, tsubst_flags_t); extern int lvalue_or_else (tree, enum lvalue_use,