Bug 93711 - [9 Regression] ICE: [[no_unique_address] when constructing via template helper
Summary: [9 Regression] ICE: [[no_unique_address] when constructing via template helper
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 9.2.0
: P2 normal
Target Milestone: 9.5
Assignee: Jason Merrill
URL:
Keywords: ice-on-valid-code
: 95878 96408 (view as bug list)
Depends on: 93667 98642
Blocks: 55713
  Show dependency treegraph
 
Reported: 2020-02-12 18:23 UTC by Evan Teran
Modified: 2023-01-16 15:19 UTC (History)
7 users (show)

See Also:
Host:
Target:
Build:
Known to work: 10.2.1
Known to fail: 10.2.0
Last reconfirmed: 2020-06-17 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Evan Teran 2020-02-12 18:23:04 UTC
This is a little hard to explain clearly, but I'll do my best.

I have a container class that I want to make properly allocator aware and ran into this ICE. When I don't use allocator_traits, and directly copy the allocator, it works just fine. But once I introduce the usage of allocator_traits<A>::select_on_container_copy_construction, I get an ICE.

Example A (Works Properly):


	#include <memory>

	template <class T, class A = std::allocator<T>>
	struct MyType {

		MyType(const MyType &other)
			: a_(other.a_) {
		}

		MyType(const A &a = A())
			: a_(a) {
		}

		T *p_;
		[[no_unique_address]] A a_;
	};

	int main() {
		MyType<int> x;
		MyType<int> y(x);
	}

Example B (ICE):


	#include <memory>

	template <class T, class A = std::allocator<T>>
	struct MyType {

		MyType(const MyType &other)
			: a_(std::allocator_traits<A>::select_on_container_copy_construction(other.a_)) {
		}

		MyType(const A &a = A())
			: a_(std::allocator_traits<A>::select_on_container_copy_construction(a)) {
		}

		T *p_;
		[[no_unique_address]] A a_;
	};

	int main() {
		MyType<int> x;
		MyType<int> y(x);
	}

Both cases work correctly if I remove [[no_unique_address], so it seems to be an interaction between [[no_unique_address]] and a_(std::allocator_traits<A>::select_on_container_copy_construction(a)).


Here is the complete backtrace for the failing example:


g++ -std=c++17 test.cpp -o test
during RTL pass: expand
test.cpp: In constructor ‘MyType<T, A>::MyType(const A&) [with T = int; A = std::allocator<int>]’:
test.cpp:12:74: internal compiler error: in assign_temp, at function.c:982
   12 |   : a_(std::allocator_traits<A>::select_on_container_copy_construction(a)) {
      |                                                                          ^
0x59a442 assign_temp(tree_node*, int, int)
        /var/tmp/portage/sys-devel/gcc-9.2.0-r2/work/gcc-9.2.0/gcc/function.c:982
0x7c050f expand_call(tree_node*, rtx_def*, int)
        /var/tmp/portage/sys-devel/gcc-9.2.0-r2/work/gcc-9.2.0/gcc/calls.c:3459
0x8c4a2d expand_expr_real_1(tree_node*, rtx_def*, machine_mode, expand_modifier, rtx_def**, bool)
        /var/tmp/portage/sys-devel/gcc-9.2.0-r2/work/gcc-9.2.0/gcc/expr.c:11033
0x8d297b expand_normal
        /var/tmp/portage/sys-devel/gcc-9.2.0-r2/work/gcc-9.2.0/gcc/expr.h:285
0x8d297b store_field
        /var/tmp/portage/sys-devel/gcc-9.2.0-r2/work/gcc-9.2.0/gcc/expr.c:7022
0x8cff37 expand_assignment(tree_node*, tree_node*, bool)
        /var/tmp/portage/sys-devel/gcc-9.2.0-r2/work/gcc-9.2.0/gcc/expr.c:5296
0x7cc3f0 expand_call_stmt
        /var/tmp/portage/sys-devel/gcc-9.2.0-r2/work/gcc-9.2.0/gcc/cfgexpand.c:2722
0x7cc3f0 expand_gimple_stmt_1
        /var/tmp/portage/sys-devel/gcc-9.2.0-r2/work/gcc-9.2.0/gcc/cfgexpand.c:3691
0x7cc3f0 expand_gimple_stmt
        /var/tmp/portage/sys-devel/gcc-9.2.0-r2/work/gcc-9.2.0/gcc/cfgexpand.c:3850
0x7d1237 expand_gimple_basic_block
        /var/tmp/portage/sys-devel/gcc-9.2.0-r2/work/gcc-9.2.0/gcc/cfgexpand.c:5890
0x7d35d7 execute
        /var/tmp/portage/sys-devel/gcc-9.2.0-r2/work/gcc-9.2.0/gcc/cfgexpand.c:6513
Please submit a full bug report,
with preprocessed source if appropriate.
Please include the complete backtrace with any bug report.
See <https://bugs.gentoo.org/> for instructions.
Comment 1 Evan Teran 2020-02-12 18:38:09 UTC
As a follow-up, it appears that the optimization level is a factor. I only get the ICE in my builds which don't pass a -O flag.

Here's a live example that can be experimented with:

https://godbolt.org/z/zAEcQd

Adding -Og, -O1, -O2, etc... all make it suddenly work.
Comment 2 Andrew Pinski 2020-02-12 19:03:15 UTC
Most likely a similar issue as PR 93667.
Comment 3 Lénárd Szolnoki 2020-06-17 16:55:32 UTC
A smaller example:

struct S {
    S(S&&) = delete;
};

S foo();

struct A {
    [[no_unique_address]] S s = foo();
};

A a{};

Version:
11.0.0 20200616

Command line options:
-std=c++20 -Wall -Wextra -pedantic

Diagnostics:
during RTL pass: expand
<source>: In function 'void __static_initialization_and_destruction_0(int, int)':
<source>:11:5: internal compiler error: in assign_temp, at function.c:984
   11 | A a{};
      |     ^
Comment 4 Marek Polacek 2020-06-17 23:37:10 UTC
Same backtrace as bug 90254 but it didn't start with the same revision.  This one started with r264813.
Comment 5 Marek Polacek 2020-06-24 18:45:31 UTC
*** Bug 95878 has been marked as a duplicate of this bug. ***
Comment 6 Marek Polacek 2020-06-24 18:45:58 UTC
Another test:

struct istream_iterator {
	istream_iterator() {}
	istream_iterator(const istream_iterator&) {}
};

istream_iterator next(istream_iterator&& bound) {
	return static_cast<istream_iterator>(bound);
}

struct copy_result {
	[[no_unique_address]] istream_iterator in;
};

int main() {
	copy_result result{next(istream_iterator{})};
}
Comment 7 Boris Staletic 2020-07-01 15:45:32 UTC
I don't know if this is the same problem, since the actual `std::istream_iterator<int>` isn't empty, unlike in the other test cases. If the following is a different problem, sorry up front for the noise.

https://godbolt.org/z/XAM9o7

This is the actual thing I've come across during a pull request for NanoRange library.

DIfferences from the above test cases:

- ICE is there even at `-Og`, as well as `-O0`
- The bottom example shows that wrapping the call to `fn()` in `std::move()` avoids the ICE.

If this is a separate issue and thus noise, I'll open a separate issue.
Comment 8 Jonathan Wakely 2020-08-07 16:01:11 UTC
*** Bug 96408 has been marked as a duplicate of this bug. ***
Comment 9 Jonathan Wakely 2020-08-07 16:04:12 UTC
Another one:

struct A { A(const A&) = delete; };

A f();

struct C
{
  [[no_unique_address]] A a;
};

C c{f()};


during RTL pass: expand
ice.C: In function 'void __static_initialization_and_destruction_0(int, int)':
ice.C:10:8: internal compiler error: in assign_temp, at function.c:984
   10 | C c{f()};
      |        ^
0x6f9772 assign_temp(tree_node*, int, int)
        /home/jwakely/src/gcc/gcc/gcc/function.c:984
0xb56653 expand_call(tree_node*, rtx_def*, int)
        /home/jwakely/src/gcc/gcc/gcc/calls.c:3750
0xc87aaa expand_expr_real_1(tree_node*, rtx_def*, machine_mode, expand_modifier, rtx_def**, bool)
        /home/jwakely/src/gcc/gcc/gcc/expr.c:11240
0xc9a133 expand_expr_real(tree_node*, rtx_def*, machine_mode, expand_modifier, rtx_def**, bool)
        /home/jwakely/src/gcc/gcc/gcc/expr.c:8474
0xc9a133 expand_normal
        /home/jwakely/src/gcc/gcc/gcc/expr.h:288
0xc9a133 store_field
        /home/jwakely/src/gcc/gcc/gcc/expr.c:7213
0xc966d6 expand_assignment(tree_node*, tree_node*, bool)
        /home/jwakely/src/gcc/gcc/gcc/expr.c:5448
0xb66553 expand_call_stmt
        /home/jwakely/src/gcc/gcc/gcc/cfgexpand.c:2701
0xb66553 expand_gimple_stmt_1
        /home/jwakely/src/gcc/gcc/gcc/cfgexpand.c:3682
0xb66553 expand_gimple_stmt
        /home/jwakely/src/gcc/gcc/gcc/cfgexpand.c:3847
0xb6bd9a expand_gimple_basic_block
        /home/jwakely/src/gcc/gcc/gcc/cfgexpand.c:5888
0xb6d846 execute
        /home/jwakely/src/gcc/gcc/gcc/cfgexpand.c:6572
Please submit a full bug report,
with preprocessed source if appropriate.
Please include the complete backtrace with any bug report.
See <https://gcc.gnu.org/bugs/> for instructions.
Comment 10 GCC Commits 2020-08-14 16:58:53 UTC
The master branch has been updated by Jason Merrill <jason@gcc.gnu.org>:

https://gcc.gnu.org/g:320054784250e572cb75d6f69ab44b2330d61d8b

commit r11-2704-g320054784250e572cb75d6f69ab44b2330d61d8b
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Aug 12 05:45:02 2020 -0400

    c++: Copy elision and [[no_unique_address]]. [PR93711]
    
    We don't elide a copy from a function returning a class by value into a base
    because that can overwrite data laid out in the tail padding of the base
    class; we need to handle [[no_unique_address]] fields the same way, or we
    ICE when the middle-end wants to create a temporary object of a
    TYPE_NEEDS_CONSTRUCTING type.
    
    This means that we can't always express initialization of a field with
    INIT_EXPR from a TARGET_EXPR the way we usually do, so I needed
    to change several places that were assuming that was sufficient.
    
    This also fixes 90254, the same problem with C++17 aggregate initialization
    of a base.
    
    gcc/cp/ChangeLog:
    
            PR c++/90254
            PR c++/93711
            * cp-tree.h (unsafe_return_slot_p): Declare.
            * call.c (is_base_field_ref): Rename to unsafe_return_slot_p.
            (build_over_call): Check unsafe_return_slot_p.
            (build_special_member_call): Likewise.
            * init.c (expand_default_init): Likewise.
            * typeck2.c (split_nonconstant_init_1): Likewise.
    
    gcc/testsuite/ChangeLog:
    
            PR c++/90254
            PR c++/93711
            * g++.dg/cpp1z/aggr-base10.C: New test.
            * g++.dg/cpp2a/no_unique_address7.C: New test.
            * g++.dg/cpp2a/no_unique_address7a.C: New test.
Comment 11 Jason Merrill 2020-08-14 17:06:41 UTC
Fixed for GCC 11 so far.
Comment 12 GCC Commits 2020-12-23 22:11:46 UTC
The releases/gcc-10 branch has been updated by Jason Merrill <jason@gcc.gnu.org>:

https://gcc.gnu.org/g:97014e4ada448aa8978b3cd14ed95e0e56f375d9

commit r10-9168-g97014e4ada448aa8978b3cd14ed95e0e56f375d9
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Aug 12 05:45:02 2020 -0400

    c++: Copy elision and [[no_unique_address]]. [PR93711]
    
    We don't elide a copy from a function returning a class by value into a base
    because that can overwrite data laid out in the tail padding of the base
    class; we need to handle [[no_unique_address]] fields the same way, or we
    ICE when the middle-end wants to create a temporary object of a
    TYPE_NEEDS_CONSTRUCTING type.
    
    This means that we can't always express initialization of a field with
    INIT_EXPR from a TARGET_EXPR the way we usually do, so I needed
    to change several places that were assuming that was sufficient.
    
    This also fixes 90254, the same problem with C++17 aggregate initialization
    of a base.
    
    gcc/cp/ChangeLog:
    
            PR c++/90254
            PR c++/93711
            * cp-tree.h (unsafe_return_slot_p): Declare.
            * call.c (is_base_field_ref): Rename to unsafe_return_slot_p.
            (build_over_call): Check unsafe_return_slot_p.
            (build_special_member_call): Likewise.
            * init.c (expand_default_init): Likewise.
            * typeck2.c (split_nonconstant_init_1): Likewise.
    
    gcc/testsuite/ChangeLog:
    
            PR c++/90254
            PR c++/93711
            * g++.dg/cpp1z/aggr-base10.C: New test.
            * g++.dg/cpp2a/no_unique_address7.C: New test.
            * g++.dg/cpp2a/no_unique_address7a.C: New test.
Comment 13 Richard Biener 2021-06-01 08:16:22 UTC
GCC 9.4 is being released, retargeting bugs to GCC 9.5.
Comment 14 Jason Merrill 2021-12-22 18:29:34 UTC
Fixed in 10.2 and later.