ErrorOr<> conflicts with unique_ptr<>

Michael,

In lld, we have places that used nested a ErrorOr<std::unique_ptr> 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 factoryU() {
std::unique_ptr 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> factoryEU() {
std::unique_ptr f(new Foo);
return f; // ERROR: call to implicitly-deleted copy constructor of 'std::__1::unique_ptr<Foo, std::__1::default_delete >’
}

void sinkU(std::unique_ptr 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> f) {
f->doit(); // ERROR: no member named ‘doit’ in ‘std::__1::unique_ptr<Foo, std::__1::default_delete >’
}

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

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.

Is there no way to promote the return value automatically? If you make the local variable be of type Error<std::unique_ptr>, you run into the errors that you can’t access its methods (below).

If ErrorOr models a pointer to T, then ErrorOr<std::unique_ptr> would be modeling a pointer to a std::unique_ptr. But unique_ptr<> already adds a pointer to the type. I thought that ErrorOr 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> 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

Agree.

Thanks

Shankar Easwaran

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

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