Bug 50025 - [DR 1288] C++0x initialization syntax doesn't work for class members of reference type
Summary: [DR 1288] C++0x initialization syntax doesn't work for class members of refer...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.6.0
: P3 normal
Target Milestone: 4.9.0
Assignee: Not yet assigned to anyone
URL:
Keywords:
: 56032 58977 60583 (view as bug list)
Depends on:
Blocks:
 
Reported: 2011-08-08 20:56 UTC by Andrzej Krzemienski
Modified: 2015-03-19 10:38 UTC (History)
13 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2012-11-15 00:00:00


Attachments
Testcase for Bug "error: invalid initialization of non-const reference of type ‘std::istream&" (784 bytes, text/plain)
2012-09-13 12:44 UTC, Uenal Mutlu
Details
This is a test case for c++/50025. (221 bytes, text/x-c++src)
2014-03-01 21:00 UTC, emsr
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Andrzej Krzemienski 2011-08-08 20:56:02 UTC
The following code does not compile although I expect it to:

/// BEGIN CODE ///
#include <utility>

struct S {};
 
struct C {
  S &  mc2;
  S && mc3;
  C( S& b, S && c ) :  mc2{b}, mc3{std::move(c)} {}
};
 
int main() { return 0; }
/// END CODE ///

I compile it with command:
g++ test.cpp -std=c++0x -o test.exe

The output is:
test.cpp: In constructor ‘C::C(S&, S&&)’:
test.cpp:9:48: error: invalid initialization of non-const reference of type ‘S&’ from an rvalue of type ‘<brace-enclosed initializer list>’
test.cpp:9:48: error: invalid initialization of reference of type ‘S&&’ from expression of type ‘<brace-enclosed initializer list>’

Compiler version (g++ -v):
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/i686-redhat-linux/4.6.0/lto-wrapper
Target: i686-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch=i686 --build=i686-redhat-linux
Thread model: posix
gcc version 4.6.0 20110428 (Red Hat 4.6.0-6) (GCC)
Comment 1 Jonathan Wakely 2011-08-08 22:29:08 UTC
G++ is actually correct according to wording in the C++11 FDIS (see 8.5.4 paragraphs 5 and 6), but we've reported it as an issue that needs to be fixed.
Comment 2 Jonathan Wakely 2011-08-08 22:46:31 UTC
It's not just class members, it applies to list-initialization of any reference type:

  int i;
  int& ir{ i }; 

The FDIS requires a temporary to be created, and a non-const reference cannot bind to a temporary.
Comment 3 Andrzej Krzemienski 2011-08-08 23:31:37 UTC
(In reply to comment #1)
> G++ is actually correct according to wording in the C++11 FDIS (see 8.5.4
> paragraphs 5 and 6), but we've reported it as an issue that needs to be fixed.

Are you sure about the paragraphs 5 and 6? (5 starts with "An object of type", 6 starts with "The lifetime of the array"?) They only say how std::initializer_list is created and how it behaves, but the 6th bullet of paragraph 3 (the one starting with "Otherwise, if the initializer list has a single element") does not require that std::initializer_list is created or used. It simply says that in code like:
  int i = 0;
  int & ri{i};

"ri" is initialized with "i". (And BTW, this example works on the same compiler.)
Comment 4 Jonathan Wakely 2011-08-16 16:21:37 UTC
sorry, I meant bullets 5 and 6 in paragraph 3

the 5th bullet applies before the 6th one, and that says for a reference type a temporary is created. The 6th bullet is never reached for references.
Your example with int& works because GCC doesn't implement the FDIS wording yet, according to the FDIS it should be rejected too, see http://gcc.gnu.org/ml/gcc-help/2011-07/msg00053.html

this is a wording problem in the FDIS and will be core issue 1288 in the next issues list
Comment 5 Paolo Carlini 2011-10-26 10:33:45 UTC
Does anybody know the DR #? We could add it to the description and suspend.
Comment 6 Daniel Krügler 2011-10-26 10:44:55 UTC
The corresponding CWG issue is

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1288
Comment 7 Marc Glisse 2012-04-14 07:07:05 UTC
Link changed now that it has been voted into the working paper:
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1288

should it be un-suspended?
Comment 8 Jonathan Wakely 2012-04-14 11:01:59 UTC
Yep, thanks, Marc.
Comment 9 Uenal Mutlu 2012-09-13 12:44:44 UTC
Created attachment 28188 [details]
Testcase for Bug "error: invalid initialization of non-const reference of type ‘std::istream&"

Mysterious compiler error in line 35 of the attached testcase (test1.cpp); marked there as "BUG":

test1.cpp:35:45: error: invalid initialization of non-const reference of type ‘std::istream& {aka std::basic_istream<char>&}’ from an rvalue of type ‘void*’
Comment 10 Jonathan Wakely 2012-09-30 10:58:18 UTC
Comment on attachment 28188 [details]
Testcase for Bug "error: invalid initialization of non-const reference of type ‘std::istream&"

(In reply to comment #9)
> Created attachment 28188 [details]
> Testcase for Bug "error: invalid initialization of non-const reference of type
> ‘std::istream&"
> 
> Mysterious compiler error in line 35 of the attached testcase (test1.cpp);
> marked there as "BUG":
> 
> test1.cpp:35:45: error: invalid initialization of non-const reference of type
> ‘std::istream& {aka std::basic_istream<char>&}’ from an rvalue of type ‘void*’

That's not mysterious, and is not related to this bug report.

Your code can be reduced to:

struct Base {
  operator void*() { return 0; }
};
struct Derived1 : Base { };
struct Derived2 : Base { };

Derived1 d1;
Derived2 d2;

Base& b = true ? d1 : d2;

The conditional expression cannot implicitly convert two objects of different types to a common base class, so instead the conversion operator is used to convert both types to void*

To force the behaviour you expect use an explicit cast on one operand:

istream& si = sfile.is_open() ? (std::istream&)sfile : sbuf;
Comment 11 Jonathan Wakely 2013-01-18 15:35:20 UTC
*** Bug 56032 has been marked as a duplicate of this bug. ***
Comment 12 Jonathan Wakely 2013-01-19 20:42:14 UTC
Jason, I'd like to try and fix this, but need some clues.

A minimal example that fails to compile is:

  struct S { } s;
  S& r{s};

I find that in call.c:reference_binding we get compatible_p = false, which seems to be wrong. Even if compatible_p is true we don't take the branch that looks like it should be used:

  /* Directly bind reference when target expression's type is compatible with
     the reference and expression is an lvalue. In DR391, the wording in
     [8.5.3/5 dcl.init.ref] is changed to also require direct bindings for
     const and rvalue references to rvalues of compatible class type.
     We should also do direct bindings for non-class xvalues.  */
  if (compatible_p
      && (is_lvalue
	  || (((CP_TYPE_CONST_NON_VOLATILE_P (to)
		&& !(flags & LOOKUP_NO_RVAL_BIND))
	       || TYPE_REF_IS_RVALUE (rto))
	      && (gl_kind
		  || (!(flags & LOOKUP_NO_TEMP_BIND)
		      && (CLASS_TYPE_P (from)
			  || TREE_CODE (from) == ARRAY_TYPE))))))

Naively I would expect compatible_p and is_lvalue to be true for the case above. Does the code earlier in the function need to be adjusted for the case where a braced-init-list has a single element of reference-compatible lvalue type?
Comment 13 Paolo Carlini 2013-11-03 12:20:21 UTC
*** Bug 58977 has been marked as a duplicate of this bug. ***
Comment 14 Ville Voutilainen 2014-03-01 11:22:39 UTC
This bug seems to be fixed with the current trunk. Both the example
in the original report and similar examples compile correctly, like

std::string& f() {static std::string s; return s;}
int main()
{
	std::string& ri{f()};
}

and

int& f() {static int i = 42; return i;}


int main()
{
	int& ri{f()};
}

Please close as resolved.
Comment 15 Ed Smith-Rowland 2014-03-01 16:52:23 UTC
I agree.  My examples are working.

Unfortunately, my account seems to not have the permissions to close a bug.  I am a maintainer.

Any ideas how I can get such permissions set?


Now if only I can remember what libstdc++ ctor I had to have one paren init amongst all the other brace inits that annoyed me enough to post this error! ;-)
Comment 16 Ed Smith-Rowland 2014-03-01 16:54:17 UTC
Sorry for the noise.  I reported a dupe not this bug.

My question on permissions still stands though.
Comment 17 Marc Glisse 2014-03-01 17:01:17 UTC
(In reply to Ed Smith-Rowland from comment #15)
> Unfortunately, my account seems to not have the permissions to close a bug. 
> I am a maintainer.
> 
> Any ideas how I can get such permissions set?

You need to log in with an @gcc.gnu.org email address.
Comment 18 emsr 2014-03-01 21:00:46 UTC
Created attachment 32240 [details]
This is a test case for c++/50025.

This just has simple classes with various reference member and verifies that using brace-initialization is accepted.  I put it under g++.dg/cpp0x.

Would anyone like more test cases?

If this passes on x86_64-linux I think I'll close this with the test case patch.

OK?
Comment 19 Jason Merrill 2014-03-01 21:11:20 UTC
Sounds good.
Comment 20 emsr 2014-03-01 22:51:56 UTC
Author: emsr
Date: Sat Mar  1 22:51:25 2014
New Revision: 208251

URL: http://gcc.gnu.org/viewcvs?rev=208251&root=gcc&view=rev
Log:
2014-03-01  Edward Smith-Rowland  <3dw4rd@verizon.net>

	PR c++/50025
	* g++.dg/cpp0x/pr50025.C: New.


Added:
    trunk/gcc/testsuite/g++.dg/cpp0x/pr50025.C
Modified:
    trunk/gcc/testsuite/ChangeLog
Comment 21 emsr 2014-03-01 22:57:35 UTC
Fixed.
Comment 22 Jonathan Wakely 2014-03-19 13:22:53 UTC
For the record, I think r207164 fixed this
Comment 23 __vic 2014-03-25 10:58:05 UTC
Is it related bug?

struct C
{
    const std::string &st_ref;

    explicit C(const std::string &st) : st_ref{st}
    {
        std::cout << &st << ' ' << &st_ref << std::endl;
        assert(&st == &st_ref);
    }
};

assert() fires! gcc 4.8.2
If we replace st_ref{st} with st_ref(st), assert() doesn't fire.
Comment 24 Jonathan Wakely 2014-03-25 11:12:22 UTC
(In reply to __vic from comment #23)
> Is it related bug?

Yes, the original C++11 rules required a temporary to be created there and the reference member binds to that temporary (which then goes out of scope).

Your code works correctly with GCC 4.9
Comment 25 Paolo Carlini 2015-03-19 10:38:49 UTC
*** Bug 60583 has been marked as a duplicate of this bug. ***