The issue here is that the C++ front end represents the ref-qualifier as a variant but includes it in the canonical type, because at the language level the types are distinct. Note that this is also true of language-independent qualifiers like "const", so this doesn't seem like an unreasonable thing for the front end to do.
But as a result, when build_type_attribute_qual_variant builds a distinct type copy of the ref-qualified type, it is now its own main variant, while the canonical type is still a variant of another type, and verify_type doesn't like that. This patch introduces a langhook to mimic for language-dependent qualifiers the b_t_a_q_v behavior for cv-qualifiers, i.e. strip them before making the copy and reintroduce them afterward. Tested x86_64-pc-linux-gnu. OK for trunk?
commit 50a71bc8cbf3d17f0039f531b96e64f348b83549 Author: Jason Merrill <ja...@redhat.com> Date: Fri Sep 8 00:09:46 2017 +0200 PR c++/70029 - ICE with ref-qualifier and -flto gcc/ * langhooks.h (struct lang_hooks_for_types): Add copy_lang_qualifiers. * attribs.c (build_type_attribute_qual_variant): Use it. * langhooks-def.h (LANG_HOOKS_COPY_LANG_QUALIFIERS): Default to NULL. (LANG_HOOKS_FOR_TYPES_INITIALIZER): Use it. * tree.c (verify_type): Re-enable TYPE_CANONICAL main variant check. gcc/cp/ * tree.c (cxx_copy_lang_qualifiers): New. * cp-tree.h: Declare it. * cp-objcp-common.h: Define LANG_HOOKS_COPY_LANG_QUALIFIERS. diff --git a/gcc/attribs.c b/gcc/attribs.c index faa0649..b8f58a7 100644 --- a/gcc/attribs.c +++ b/gcc/attribs.c @@ -959,8 +959,9 @@ build_decl_attribute_variant (tree ddecl, tree attribute) Record such modified types already made so we don't make duplicates. */ tree -build_type_attribute_qual_variant (tree ttype, tree attribute, int quals) +build_type_attribute_qual_variant (tree otype, tree attribute, int quals) { + tree ttype = otype; if (! attribute_list_equal (TYPE_ATTRIBUTES (ttype), attribute)) { tree ntype; @@ -983,6 +984,11 @@ build_type_attribute_qual_variant (tree ttype, tree attribute, int quals) } ttype = build_qualified_type (ttype, TYPE_UNQUALIFIED); + if (lang_hooks.types.copy_lang_qualifiers + && otype != TYPE_MAIN_VARIANT (otype)) + ttype = (lang_hooks.types.copy_lang_qualifiers + (ttype, TYPE_MAIN_VARIANT (otype))); + ntype = build_distinct_type_copy (ttype); TYPE_ATTRIBUTES (ntype) = attribute; @@ -1000,6 +1006,9 @@ build_type_attribute_qual_variant (tree ttype, tree attribute, int quals) TYPE_CANONICAL (ntype) = TYPE_CANONICAL (ttype); ttype = build_qualified_type (ntype, quals); + if (lang_hooks.types.copy_lang_qualifiers + && otype != TYPE_MAIN_VARIANT (otype)) + ttype = lang_hooks.types.copy_lang_qualifiers (ttype, otype); } else if (TYPE_QUALS (ttype) != quals) ttype = build_qualified_type (ttype, quals); diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h index 10fcdf3..3e4cc9c 100644 --- a/gcc/cp/cp-objcp-common.h +++ b/gcc/cp/cp-objcp-common.h @@ -99,6 +99,8 @@ extern void cp_register_dumps (gcc::dump_manager *); #define LANG_HOOKS_BUILTIN_FUNCTION_EXT_SCOPE cxx_builtin_function_ext_scope #undef LANG_HOOKS_TYPE_HASH_EQ #define LANG_HOOKS_TYPE_HASH_EQ cxx_type_hash_eq +#undef LANG_HOOKS_COPY_LANG_QUALIFIERS +#define LANG_HOOKS_COPY_LANG_QUALIFIERS cxx_copy_lang_qualifiers #undef LANG_HOOKS_MISSING_NORETURN_OK_P #define LANG_HOOKS_MISSING_NORETURN_OK_P cp_missing_noreturn_ok_p #undef LANG_HOOKS_BLOCK_MAY_FALLTHRU diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index a0e31d3..a57de33 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6963,6 +6963,7 @@ extern tree convert_bitfield_to_declared_type (tree); extern tree cp_save_expr (tree); extern bool cast_valid_in_integral_constant_expression_p (tree); extern bool cxx_type_hash_eq (const_tree, const_tree); +extern tree cxx_copy_lang_qualifiers (const_tree, const_tree); extern void cxx_print_statistics (void); extern bool maybe_warn_zero_as_null_pointer_constant (tree, location_t); diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 12c31fb..f387f38 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -4626,6 +4626,21 @@ cxx_type_hash_eq (const_tree typea, const_tree typeb) TYPE_RAISES_EXCEPTIONS (typeb), ce_exact); } +/* Copy the language-specific type variant modifiers from TYPEB to TYPEA. For + C++, these are the exception-specifier and ref-qualifier. */ + +tree +cxx_copy_lang_qualifiers (const_tree typea, const_tree typeb) +{ + tree type = CONST_CAST_TREE (typea); + if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE) + { + type = build_exception_variant (type, TYPE_RAISES_EXCEPTIONS (typeb)); + type = build_ref_qualified_type (type, type_memfn_rqual (typeb)); + } + return type; +} + /* Apply FUNC to all language-specific sub-trees of TP in a pre-order traversal. Called from walk_tree. */ diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h index ea2006c..61b081b 100644 --- a/gcc/langhooks-def.h +++ b/gcc/langhooks-def.h @@ -186,6 +186,7 @@ extern tree lhd_unit_size_without_reusable_padding (tree); lhd_omp_firstprivatize_type_sizes #define LANG_HOOKS_OMP_MAPPABLE_TYPE lhd_omp_mappable_type #define LANG_HOOKS_TYPE_HASH_EQ NULL +#define LANG_HOOKS_COPY_LANG_QUALIFIERS NULL #define LANG_HOOKS_GET_ARRAY_DESCR_INFO NULL #define LANG_HOOKS_GET_SUBRANGE_BOUNDS NULL #define LANG_HOOKS_GET_TYPE_BIAS NULL @@ -211,6 +212,7 @@ extern tree lhd_unit_size_without_reusable_padding (tree); LANG_HOOKS_OMP_FIRSTPRIVATIZE_TYPE_SIZES, \ LANG_HOOKS_OMP_MAPPABLE_TYPE, \ LANG_HOOKS_TYPE_HASH_EQ, \ + LANG_HOOKS_COPY_LANG_QUALIFIERS, \ LANG_HOOKS_GET_ARRAY_DESCR_INFO, \ LANG_HOOKS_GET_SUBRANGE_BOUNDS, \ LANG_HOOKS_GET_TYPE_BIAS, \ diff --git a/gcc/langhooks.h b/gcc/langhooks.h index 88f6f71..b0c9829 100644 --- a/gcc/langhooks.h +++ b/gcc/langhooks.h @@ -123,6 +123,10 @@ struct lang_hooks_for_types FUNCTION_TYPE or METHOD_TYPE. */ bool (*type_hash_eq) (const_tree, const_tree); + /* If non-NULL, return TYPE1 with any language-specific modifiers copied from + TYPE2. */ + tree (*copy_lang_qualifiers) (const_tree, const_tree); + /* Return TRUE if TYPE uses a hidden descriptor and fills in information for the debugger about the array bounds, strides, etc. */ bool (*get_array_descr_info) (const_tree, struct array_descr_info *); diff --git a/gcc/testsuite/g++.dg/lto/pr70029_0.C b/gcc/testsuite/g++.dg/lto/pr70029_0.C new file mode 100644 index 0000000..9c8c31c --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr70029_0.C @@ -0,0 +1,10 @@ +// PR c++/70029 +// { dg-lto-do assemble } + +struct A +{ + A(); + int foo() && __attribute__ ((__warn_unused_result__)) { return 0; } +}; + +A a; diff --git a/gcc/tree.c b/gcc/tree.c index 7a70eb7..721330a 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -13220,9 +13220,7 @@ verify_type (const_tree t) debug_tree (ct); error_found = true; } - /* FIXME: this is violated by the C++ FE as discussed in PR70029, when - FUNCTION_*_QUALIFIED flags are set. */ - if (0 && TYPE_MAIN_VARIANT (t) == t && ct && TYPE_MAIN_VARIANT (ct) != ct) + if (TYPE_MAIN_VARIANT (t) == t && ct && TYPE_MAIN_VARIANT (ct) != ct) { error ("TYPE_CANONICAL of main variant is not main variant"); debug_tree (ct);