Bug 63506 - GCC deduces wrong return type of operator*() inside template functions
Summary: GCC deduces wrong return type of operator*() inside template functions
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.9.1
: P3 normal
Target Milestone: 6.0
Assignee: Not yet assigned to anyone
URL:
Keywords: rejects-valid
Depends on:
Blocks:
 
Reported: 2014-10-10 14:01 UTC by Steffen Müthing
Modified: 2015-12-15 10:19 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work: 4.6.4, 4.7.4
Known to fail: 4.8.1, 4.9.1, 5.0
Last reconfirmed: 2014-12-19 00:00:00


Attachments
Minimal working example based on std::vector<bool> (175 bytes, text/x-csrc)
2014-10-10 14:01 UTC, Steffen Müthing
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Steffen Müthing 2014-10-10 14:01:58 UTC
Created attachment 33681 [details]
Minimal working example based on std::vector<bool>

When trying to convert some iterators in our numerics library DUNE (dune-project.org) to return temporaries instead of const refs, I stumbled over the following bug in GCC 4.8.3 and 4.9.1:

Given a class with an operator*() that returns a temporary:

struct iterator
{
  ...

  proxy operator*() const
  {
    return ...;
  }

  proxy get() const
  {
    return ...;
  }

};

the expression 'decltype(*it())' should yield 'proxy', and a variable declared with 'auto&& x = *it;' should be of type 'proxy&&'. The above is true inside normal functions:

void bar(iterator it)
{
  typedef decltype(*it()) P; // P == proxy
  auto&& x = *it; // decltype(x) == proxy&&
}

But inside a function template, 'decltype(*it())' becomes 'proxy&', and 'auto&& x = *it' fails with a compiler error:

template<typename T>
void foo(T t, iterator it)
{
  typedef decltype(*it()) P; // P == proxy&
  auto&& x = *it; // compiler error, see below
}

error: invalid initialization of non-const reference of type 'proxy&' from an rvalue of type 'proxy'

For some reason, the compiler deduces the wrong type early on and then fails when it later realizes the correct return type...

This problem can easily be observed with a std::vector<bool>, which returns a proxy from its iterator. I have attached a minimal working example: The contained code (which creates a vector and iterates over its contents with a range-based for loop using auto&&) works in main() and in a normal function bar(), but it fails to compile in foo(), which is a function template.

GCC 4.6 and 4.7 compile foo() correctly.
Comment 1 Steffen Müthing 2014-10-10 14:03:22 UTC
I forgot to mention: This problem is specific to operator*(). If you add an equivalent normal member function and replace the dereferencing operations with calls to that function, the problem does not occur.
Comment 2 Marc Glisse 2014-10-10 14:19:46 UTC
Do you mean decltype(*it) (without the extra parentheses) everywhere?
Comment 3 Steffen Müthing 2014-10-10 14:27:32 UTC
Ooops, yes, I did. Actually I meant to write decltype(*iterator()), but decltype(*it()) doesn't make sense, of course.

And to avoid further confusion: The function get() in the iterator is just there as an example for a function that avoids the problem (see comment #1).
Comment 4 Daniel Krügler 2014-10-11 09:58:05 UTC
Here is a complete example demonstrating the problem, which is also present in gcc 5.0.0 20141010 (experimental):

//-------------------------------
struct proxy {};

struct iterator
{
  proxy operator*() { return proxy(); }
};

//#define DEACTIVATE

#ifndef DEACTIVATE
template<typename T = int>
#endif
void foo(iterator it)
{
  auto&& x = *it;
}

int main() 
{
  iterator it;
  foo(it);
}
//-------------------------------
Comment 5 Steffen Müthing 2014-10-13 10:01:52 UTC
The exact same problem is present on operator[] :

//-------------------------------
struct proxy {};

struct iterator
{
  proxy operator*() { return proxy(); }

  proxy operator[](int i) { return proxy(); }
};

//#define DEACTIVATE

#ifndef DEACTIVATE
template<typename T = int>
#endif
void foo(iterator it)
{
  auto&& x = *it;
  auto&& y = it[1];
}

int main()
{
  iterator it;
  foo(it);
}
//-------------------------------
Comment 6 Ville Voutilainen 2014-12-19 13:45:35 UTC
Clang accepts the code with or without DEACTIVATE defined.
Comment 7 Paolo Carlini 2015-12-15 10:09:36 UTC
This is fixed in mainline. I'm adding the reduced testcases and closing the bug.
Comment 8 paolo@gcc.gnu.org 2015-12-15 10:18:44 UTC
Author: paolo
Date: Tue Dec 15 10:18:13 2015
New Revision: 231646

URL: https://gcc.gnu.org/viewcvs?rev=231646&root=gcc&view=rev
Log:
2015-12-15  Paolo Carlini  <paolo.carlini@oracle.com>

	PR c++/63506
	* g++.dg/cpp0x/pr63506-1.C: New.
	* g++.dg/cpp0x/pr63506-2.C: Likewise.

Added:
    trunk/gcc/testsuite/g++.dg/cpp0x/pr63506-1.C
    trunk/gcc/testsuite/g++.dg/cpp0x/pr63506-2.C
Modified:
    trunk/gcc/testsuite/ChangeLog
Comment 9 Paolo Carlini 2015-12-15 10:19:26 UTC
Done.