Bug 52988 - std::async not executed on function returning nullptr_t
Summary: std::async not executed on function returning nullptr_t
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.6.3
: P3 normal
Target Milestone: 4.6.4
Assignee: Jason Merrill
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2012-04-14 16:19 UTC by surfintheusa
Modified: 2012-06-25 20:43 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 4.6.3, 4.8.0
Last reconfirmed: 2012-04-14 00:00:00


Attachments
Sample code demonstrating bug and workaround. (202 bytes, text/plain)
2012-04-14 16:19 UTC, surfintheusa
Details

Note You need to log in before you can comment on or make changes to this bug.
Description surfintheusa 2012-04-14 16:19:02 UTC
Created attachment 27157 [details]
Sample code demonstrating bug and workaround.

Given a function which returns a nullptr_t, the following appears to not call the function:

future<nullptr_t> fnp = async(returns_nullptr_t);
fnp.get();


For a function which returns an int, the behavior is as expected.

A workaround is to call fnp.wait() before fnp.get(). In that case, the function is indeed called (and its side-effects are observed).


The attached file demonstrates the bug clearly. When executed, one should see the output:

returns_nullptr_t
returns_int

but only the second line shows. If the workaround is applied, then both lines show.
Comment 1 Jonathan Wakely 2012-04-14 17:06:21 UTC
strange ... I'm looking into it
Comment 2 Jonathan Wakely 2012-04-14 17:29:52 UTC
Jason, could ou take a look at this please?

This looks like a G++ issue, doing something weird when nullptr_t is involved.  Possibly assuming that since all nullptr_t values are the same there's no point calling a function to get one and it's valid to just return nullptr immediately.


future<int>::get() produces this:

_Res std::future<_Res>::get() [with _Res = int] (struct future * const this)
{
  struct __basic_future * D.117159;
  int D.117160;
  struct __basic_future * D.117161;
  struct _Result & D.117162;
  int & D.117163;
  type & D.117164;
  struct _Reset __reset;

  try
    {
      D.117159 = &this->D.52581;
      std::__basic_future<int>::_Reset::_Reset (&__reset, D.117159);
      try
        {
          D.117161 = &this->D.52581;
          D.117162 = std::__basic_future<int>::_M_get_result (D.117161);
          D.117163 = std::__future_base::_Result<int>::_M_value (D.117162);
          D.117164 = std::move<int&> (D.117163);
          D.117160 = *D.117164;
          return D.117160;
        }
      finally
        {
          std::__basic_future<int>::_Reset::~_Reset (&__reset);
        }
    }
  finally
    {
      __reset = {CLOBBER};
    }
}


But even though they're generated from the same template future<nullptr_t>::get() looks like this, with no call to _M_get_result() before returning:


_Res std::future<_Res>::get() [with _Res = std::nullptr_t] (struct future * const this)
{ 
  struct __basic_future * D.117150;
  <<< Unknown tree: nullptr_type >>> D.117151;
  struct _Reset __reset;
      
  try 
    { 
      D.117150 = &this->D.52293;
      std::__basic_future<std::nullptr_t>::_Reset::_Reset (&__reset, D.117150);
      try
        {
          D.117151 = 0;
          return D.117151;
        }
      finally
        {
          std::__basic_future<std::nullptr_t>::_Reset::~_Reset (&__reset);
        }
    }
  finally
    { 
      __reset = {CLOBBER};
    }
}
Comment 3 Jonathan Wakely 2012-04-14 17:40:18 UTC
Slightly reduced testcase without <iostream>

#include <future>

using namespace std;

nullptr_t returns_nullptr_t()
{
        __builtin_puts("returns_nullptr_t");
        return nullptr;
}

int returns_int()
{
        __builtin_puts("returns_int");
        return 22;
}

int main()
{
        future<nullptr_t> fnp = async(returns_nullptr_t);
        //fnp.wait();   // The workaround.
        fnp.get();

        future<int> fi = async(returns_int);
        fi.get();

    return 0;
}
Comment 4 Jonathan Wakely 2012-04-28 20:17:35 UTC
Unassigning myself as I think this is a FE issue.

I haven't been able to reduce this to something smaller but -fdump-tree-original also shows the nullptr_t case just returns without invoking the deferred function:

for future<int> it's OK

;; Function _Res std::future<_Res>::get() [with _Res = int] (null)
;; enabled by -tree-original

{
  struct _Reset __reset;

    struct _Reset __reset;
  <<cleanup_point <<< Unknown tree: expr_stmt
  std::__basic_future<int>::_Reset::_Reset (&__reset, (struct __basic_future &) &((struct future *) this)->D.52275) >>>>>;
  try
    {
      <<cleanup_point return <retval> = *std::move<int&> ((int &) (int *) std::__future_base::_Result<int>::_M_value ((struct _Result *) std::__basic_future<int>::_M_get_result (&((struct future *) this)->D.52275)))>>;
    }
  finally
    {
      std::__basic_future<int>::_Reset::~_Reset (&__reset);
    }
}

And for future<nullptr_t> it's not

;; Function _Res std::future<_Res>::get() [with _Res = std::nullptr_t] (null)
;; enabled by -tree-original

{
  struct _Reset __reset;

    struct _Reset __reset;
  <<cleanup_point <<< Unknown tree: expr_stmt
  std::__basic_future<std::nullptr_t>::_Reset::_Reset (&__reset, (struct __basic_future &) &((struct future *) this)->D.51987) >>>>>;
  try
    {
      return <retval> = 0;
    }
  finally
    {
      std::__basic_future<std::nullptr_t>::_Reset::~_Reset (&__reset);
    }
}
Comment 5 Jason Merrill 2012-06-25 20:37:18 UTC
Author: jason
Date: Mon Jun 25 20:37:14 2012
New Revision: 188953

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=188953
Log:
	PR c++/52988
	* typeck.c (decay_conversion): Don't discard side-effects from
	expressions of nullptr_t.

Added:
    branches/gcc-4_6-branch/gcc/testsuite/g++.dg/cpp0x/nullptr28.C
Modified:
    branches/gcc-4_6-branch/gcc/cp/ChangeLog
    branches/gcc-4_6-branch/gcc/cp/typeck.c
    branches/gcc-4_6-branch/gcc/testsuite/ChangeLog
Comment 6 Jason Merrill 2012-06-25 20:37:30 UTC
Author: jason
Date: Mon Jun 25 20:37:25 2012
New Revision: 188954

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=188954
Log:
	PR c++/52988
	* typeck.c (decay_conversion): Don't discard side-effects from
	expressions of nullptr_t.

Added:
    branches/gcc-4_7-branch/gcc/testsuite/g++.dg/cpp0x/nullptr28.C
Modified:
    branches/gcc-4_7-branch/gcc/cp/ChangeLog
    branches/gcc-4_7-branch/gcc/cp/typeck.c
    branches/gcc-4_7-branch/gcc/testsuite/ChangeLog
Comment 7 Jason Merrill 2012-06-25 20:39:53 UTC
Author: jason
Date: Mon Jun 25 20:39:47 2012
New Revision: 188955

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=188955
Log:
	PR c++/52988
	* typeck.c (decay_conversion): Don't discard side-effects from
	expressions of nullptr_t.

Added:
    trunk/gcc/testsuite/g++.dg/cpp0x/nullptr28.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/typeck.c
    trunk/gcc/testsuite/ChangeLog
Comment 8 Jason Merrill 2012-06-25 20:43:00 UTC
Fixed for 4.6.4.