[LLVMdev] ErrorOr<> conflicts with unique_ptr<>

92 views
Skip to first unread message

Nick Kledzik

unread,
Nov 21, 2013, 6:57:16 PM11/21/13
to Michael Spencer, LLVM Developers Mailing List
Michael,

In lld, we have places that used nested a ErrorOr<std::unique_ptr<xx>> and I often hit compiler errors that require breaking up expressions to work around.   Do you have suggestions on how to code the following simple examples to not error?  Can some of these be fixed in ErrorOr.h?  Or am I totally not getting something?

-Nick


struct Foo { void doit(); };


std::unique_ptr<Foo> factoryU() {
  std::unique_ptr<Foo> f(new Foo);
  return f;  // works as expected
}

ErrorOr<Foo*> factoryE() {
  ErrorOr<Foo*> f = new Foo;
  return f;  // works as expected
}

ErrorOr<std::unique_ptr<Foo>> factoryEU() {
  std::unique_ptr<Foo> f(new Foo);
  return f; // ERROR: call to implicitly-deleted copy constructor of 'std::__1::unique_ptr<Foo, std::__1::default_delete<Foo> >’
}


void sinkU(std::unique_ptr<Foo> f) {
  f->doit();  // works as expected
}

void sinkE(ErrorOr<Foo*> f) {
  f->doit(); // ERROR: member reference base type 'typename remove_reference<Foo *>::type' (aka 'Foo *') is not a structure or union'
}

void sinkEU(ErrorOr<std::unique_ptr<Foo>> f) {
  f->doit(); // ERROR: no member named 'doit' in 'std::__1::unique_ptr<Foo, std::__1::default_delete<Foo> >'
}


Shankar Easwaran

unread,
Nov 21, 2013, 7:06:46 PM11/21/13
to Nick Kledzik, Michael Spencer, LLVM Developers Mailing List
On 11/21/2013 5:57 PM, Nick Kledzik wrote:
> std::unique_ptr<Foo> factoryU() {
> std::unique_ptr<Foo> f(new Foo);
> return f; // works as expected
> }
>
> ErrorOr<Foo*> factoryE() {
> ErrorOr<Foo*> f = new Foo;
> return f; // works as expected
> }
>
> ErrorOr<std::unique_ptr<Foo>> factoryEU() {
> std::unique_ptr<Foo> f(new Foo);
> return f; // ERROR: call to implicitly-deleted copy constructor of 'std::__1::unique_ptr<Foo, std::__1::default_delete<Foo> >’
> }
I think return std::move(f) would fix this, isnt it ?

>
> void sinkU(std::unique_ptr<Foo> f) {
> f->doit(); // works as expected
> }
>
> void sinkE(ErrorOr<Foo*> f) {
> f->doit(); // ERROR: member reference base type 'typename remove_reference<Foo *>::type' (aka 'Foo *') is not a structure or union'
> }
>
> void sinkEU(ErrorOr<std::unique_ptr<Foo>> f) {
> f->doit(); // ERROR: no member named 'doit' in 'std::__1::unique_ptr<Foo, std::__1::default_delete<Foo> >'
> }
if (error_code(f))
return;
(*f)->doit()


Thanks

Shankar Easwaran

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by the Linux Foundation

_______________________________________________
LLVM Developers mailing list
LLV...@cs.uiuc.edu http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev

David Blaikie

unread,
Nov 21, 2013, 7:07:28 PM11/21/13
to Nick Kledzik, LLVM Developers Mailing List
On Thu, Nov 21, 2013 at 3:57 PM, Nick Kledzik <kle...@apple.com> wrote:
Michael,

In lld, we have places that used nested a ErrorOr<std::unique_ptr<xx>> and I often hit compiler errors that require breaking up expressions to work around.   Do you have suggestions on how to code the following simple examples to not error?  Can some of these be fixed in ErrorOr.h?  Or am I totally not getting something?

-Nick


struct Foo { void doit(); };


std::unique_ptr<Foo> factoryU() {
  std::unique_ptr<Foo> f(new Foo);
  return f;  // works as expected
}

ErrorOr<Foo*> factoryE() {
  ErrorOr<Foo*> f = new Foo;
  return f;  // works as expected
}

ErrorOr<std::unique_ptr<Foo>> factoryEU() {
  std::unique_ptr<Foo> f(new Foo);
  return f; // ERROR: call to implicitly-deleted copy constructor of 'std::__1::unique_ptr<Foo, std::__1::default_delete<Foo> >’

While a local variable inside a function is implicitly moved when returned, that only happens when the return expression is the local variable and the same type. In this case you have an implicit conversion that would work like any other conversion of an lvalue.

So you have to write return std::move(f); unfortunately. (or you could be more explicit/verbose and say return ErrorOr<...>(std::move(f)); )
 
}


void sinkU(std::unique_ptr<Foo> f) {
  f->doit();  // works as expected
}

void sinkE(ErrorOr<Foo*> f) {
  f->doit(); // ERROR: member reference base type 'typename remove_reference<Foo *>::type' (aka 'Foo *') is not a structure or union'

It's questionable whether this should work. ErrorOr<T> models a pointer to T. So if you had ErrorOr<Foo> f you'd expect to be able to do f->doit(), but if it's an ErrorOr<Foo*>, jsut like if it were a Foo**, you'd expect to have to use (*f)->doit().
 
}

void sinkEU(ErrorOr<std::unique_ptr<Foo>> f) {
  f->doit(); // ERROR: no member named 'doit' in 'std::__1::unique_ptr<Foo, std::__1::default_delete<Foo> >'

Same here.

Nick Kledzik

unread,
Nov 21, 2013, 7:33:59 PM11/21/13
to David Blaikie, LLVM Developers Mailing List
On Nov 21, 2013, at 4:07 PM, David Blaikie <dbla...@gmail.com> wrote:




On Thu, Nov 21, 2013 at 3:57 PM, Nick Kledzik <kle...@apple.com> wrote:
Michael,

In lld, we have places that used nested a ErrorOr<std::unique_ptr<xx>> and I often hit compiler errors that require breaking up expressions to work around.   Do you have suggestions on how to code the following simple examples to not error?  Can some of these be fixed in ErrorOr.h?  Or am I totally not getting something?

-Nick


struct Foo { void doit(); };


std::unique_ptr<Foo> factoryU() {
  std::unique_ptr<Foo> f(new Foo);
  return f;  // works as expected
}

ErrorOr<Foo*> factoryE() {
  ErrorOr<Foo*> f = new Foo;
  return f;  // works as expected
}

ErrorOr<std::unique_ptr<Foo>> factoryEU() {
  std::unique_ptr<Foo> f(new Foo);
  return f; // ERROR: call to implicitly-deleted copy constructor of 'std::__1::unique_ptr<Foo, std::__1::default_delete<Foo> >’

While a local variable inside a function is implicitly moved when returned, that only happens when the return expression is the local variable and the same type. In this case you have an implicit conversion that would work like any other conversion of an lvalue.

So you have to write return std::move(f); unfortunately. (or you could be more explicit/verbose and say return ErrorOr<...>(std::move(f)); )
Is there no way to promote the return value automatically?  If you make the local variable be of type Error<std::unique_ptr<Foo>>, you run into the errors that you can’t access its methods (below).


 
}


void sinkU(std::unique_ptr<Foo> f) {
  f->doit();  // works as expected
}

void sinkE(ErrorOr<Foo*> f) {
  f->doit(); // ERROR: member reference base type 'typename remove_reference<Foo *>::type' (aka 'Foo *') is not a structure or union'

It's questionable whether this should work. ErrorOr<T> models a pointer to T. So if you had ErrorOr<Foo> f you'd expect to be able to do f->doit(), but if it's an ErrorOr<Foo*>, jsut like if it were a Foo**, you'd expect to have to use (*f)->doit().
If ErrorOr<T> models a pointer to T, then ErrorOr<std::unique_ptr<Foo>> would be modeling a pointer to a std::unique_ptr<Foo>.  But unique_ptr<> already adds a pointer to the type.   I thought that ErrorOr<T> did not add a pointer, but rather implemented operator->() to access the underlying type.  

Is there some way to do partial specialization of ErrorOr<std::unique_ptr<T>> to make -> see through both ErrorOr and unique_ptr?

My overall point is that unique_ptr<> is cool.  ErrorOr<> is cool.  But when you combine the two, the cool transparency disappears ;-(

-Nick

Shankar Easwaran

unread,
Nov 21, 2013, 8:33:24 PM11/21/13
to Nick Kledzik, David Blaikie, LLVM Developers Mailing List
On 11/21/2013 6:33 PM, Nick Kledzik wrote:
> Is there some way to do partial specialization of
> ErrorOr<std::unique_ptr<T>> to make -> see through both ErrorOr and
> unique_ptr? My overall point is that unique_ptr<> is cool. ErrorOr<>
> is cool. But when you combine the two, the cool transparency
> disappears ;-(
Agree.

Thanks

Shankar Easwaran

David Blaikie

unread,
Nov 22, 2013, 12:20:24 PM11/22/13
to Nick Kledzik, Richard Smith, LLVM Developers Mailing List
On Thu, Nov 21, 2013 at 4:33 PM, Nick Kledzik <kle...@apple.com> wrote:

On Nov 21, 2013, at 4:07 PM, David Blaikie <dbla...@gmail.com> wrote:




On Thu, Nov 21, 2013 at 3:57 PM, Nick Kledzik <kle...@apple.com> wrote:
Michael,

In lld, we have places that used nested a ErrorOr<std::unique_ptr<xx>> and I often hit compiler errors that require breaking up expressions to work around.   Do you have suggestions on how to code the following simple examples to not error?  Can some of these be fixed in ErrorOr.h?  Or am I totally not getting something?

-Nick


struct Foo { void doit(); };


std::unique_ptr<Foo> factoryU() {
  std::unique_ptr<Foo> f(new Foo);
  return f;  // works as expected
}

ErrorOr<Foo*> factoryE() {
  ErrorOr<Foo*> f = new Foo;
  return f;  // works as expected
}

ErrorOr<std::unique_ptr<Foo>> factoryEU() {
  std::unique_ptr<Foo> f(new Foo);
  return f; // ERROR: call to implicitly-deleted copy constructor of 'std::__1::unique_ptr<Foo, std::__1::default_delete<Foo> >’

While a local variable inside a function is implicitly moved when returned, that only happens when the return expression is the local variable and the same type. In this case you have an implicit conversion that would work like any other conversion of an lvalue.

So you have to write return std::move(f); unfortunately. (or you could be more explicit/verbose and say return ErrorOr<...>(std::move(f)); )
Is there no way to promote the return value automatically?

None that I know of. There's just one special case in the language - where you directly return a local variable and that variable is of the return type (no implicit conversions, etc). Might be something that could be improved (CC'd Richard Smith in case he can comment on future work in the C++ standard space).
 
 If you make the local variable be of type Error<std::unique_ptr<Foo>>, you run into the errors that you can’t access its methods (below).


 
}


void sinkU(std::unique_ptr<Foo> f) {
  f->doit();  // works as expected
}

void sinkE(ErrorOr<Foo*> f) {
  f->doit(); // ERROR: member reference base type 'typename remove_reference<Foo *>::type' (aka 'Foo *') is not a structure or union'

It's questionable whether this should work. ErrorOr<T> models a pointer to T. So if you had ErrorOr<Foo> f you'd expect to be able to do f->doit(), but if it's an ErrorOr<Foo*>, jsut like if it were a Foo**, you'd expect to have to use (*f)->doit().
If ErrorOr<T> models a pointer to T, then ErrorOr<std::unique_ptr<Foo>> would be modeling a pointer to a std::unique_ptr<Foo>.  But unique_ptr<> already adds a pointer to the type.   I thought that ErrorOr<T> did not add a pointer, but rather implemented operator->() to access the underlying type.

I don't really understand this last comment. Perhaps it's because I did a poor job explaining, I'm not sure.

Let's say I have some type Foo with a member function bar.

If I have a raw T* 't', then I would expect to write "t->bar()" (or "(*t).bar()") to call bar.

If I have an ErrorOr<T> 'e', then ErrorOr provides operator* and operator-> to access the underlying T. So I would write "e->bar()" or "(*e).bar()". ErroOr acts as though it's a pointer to T.

If I had a T** 't', then I would expect to write "(*t)->bar()" or "(**t).bar()".

So by analogy, if I have an ErrorOr<T*> I would expect to write "(*e)->bar()" or "(**e).bar()".

Now replace T* with unique_ptr<T> in the above examples and everything remains the same. ErrorOr<unique_ptr<T>> is like a pointer to a pointer to T.
 
 

Is there some way to do partial specialization of ErrorOr<std::unique_ptr<T>> to make -> see through both ErrorOr and unique_ptr?

We could, but it would be confusing, because then "e->bar()" wouldn't be the same as "(*e).bar()" (you'd still have to write "(**e).bar()" or "(*e)->bar()").
 

My overall point is that unique_ptr<> is cool.  ErrorOr<> is cool.  But when you combine the two, the cool transparency disappears ;-(

I disagree that "the cool transparency disappears". unique_ptr<T> models a pointer to T (by exposing op*/op-> to access the underlying object), ErroOr<T> models a pointer to T (by exposing op*/op-> to access the underlying object). So ErrorOr<unique_ptr<T>> models a pointer to pointer to T and thus requires two dereferences.

But yes, broadly it is kind of annoying - personally I have fantasy ideas of overloading operator '.', having a unique_ptr that was never-null and thus didn't model a pointer (and overloaded operator '.' for access to the underlying object) and then composing this unique_ref with ErrorOr would just add one level of pointery-ness, not two. 

- Dave

Rafael Espíndola

unread,
Nov 26, 2013, 11:29:19 AM11/26/13
to David Blaikie, LLVM Developers Mailing List
> I disagree that "the cool transparency disappears". unique_ptr<T> models a
> pointer to T (by exposing op*/op-> to access the underlying object),
> ErroOr<T> models a pointer to T (by exposing op*/op-> to access the
> underlying object). So ErrorOr<unique_ptr<T>> models a pointer to pointer to
> T and thus requires two dereferences.

IMHO ErrorOr should just not model a pointer. I mean, it is T or an
error, not a pointer to T. I started a patch for it but got
sidetracked into the Mangler. I will hopefully have time to get back
to it this week.

Cheers,
Rafael
Reply all
Reply to author
Forward
0 new messages