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.
strange ... I'm looking into it
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}; } }
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; }
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); } }
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
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
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
Fixed for 4.6.4.