Boost logo

Boost :

Subject: Re: [boost] [outcome] To variant, or not to variant?
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2017-06-02 16:40:29


2017-06-02 14:02 GMT+02:00 Peter Dimov via Boost <boost_at_[hidden]>:

> Andrzej Krzemienski wrote:
> ...
>
>> > That's what I was thinking - that you want to place an assert there - I
>> > just wanted to confirm that this is the only objection.
>>
>> It is not _the only_ objection. I thought that this one would be the
>> easiest to communicate across.
>>
>
> What are the others?
>

In code reviews, when I see someone using the function out of contract, I
know that this someone has a bug.

When wide contracts are employed, even if people inadvertently do silly
things, I can never easily detect bugs, because they operate within
contract by definition. Now maybe they have bug, and maybe they just
learned a way to do a hacky usage of the "rescue semantics" (he effect of
artificially widening the contract).

>
> > I can understand the desire to assert() inside, but at the same time, >
>> view the idea of returning a X* to something that is not an X ....
>>
>> Here, the first disagreement. The way I see it is that the blame is on
>> the user side. I would put it, "requesting the call X* to something you
>> know not to be an X is misguided".
>>
>
> Where is the disagreement? The blame is on the user side. This does not
> change what actually transpires.
>
> > as monumentally misguided in an error handling library.
>>
>> A question to you. Is your verdict affected by the fact that this is "an
>> error handling library" or that this is a "vocabulary type"?
>>
>
> No, this being an error handling library just makes the argument easier.
>
> In yet other words, do you object the same to vector::operator[] allowing
>> UB when over-indexed?
>>
>
> Possibly, today. In the past, not. It depends on whether the optimizer is
> good enough to avoid the repeated bounds check, and because of aliasing
> concerns, it may not be likely that it will ever be able to optimize it out
> in this case.
>
> vector::operator[] accesses occur in tight inner loops, the check is hard
> to optimize out, so we swallow the UB. Doesn't mean we have to like it,
> it's a necessary evil here.
>

Should I read the above as saying that in case of vector it would cause too
much potential run-time overhead; whereas in case of `expected` it implies
the existence of function `open_file` in the vicinity, and compared to the
cost of that function, the cost of the defensive check is negligible? Is
that what you are saying?

>
> I fail to be convinced that "error handling library" should make different
>> decisions than any other library.
>>
>
> It shouldn't, I already said the operator-> for smart pointers should
> behave the same.
>
> > The goal here is to help people write robust code,
>>
>> Here is the second disagreement. That is, I do agree with you (and I
>> guess everybody agrees) that this is always the important goal: help people
>> write robust code. I just fail to see how the decision to assign *just any*
>> semantics to something that is a misguided decision (I mean "requesting the
>> call X* to something you know not to be an X") helps people write robust
>> code.
>>
>
> "Robust" is not the same as "correct". Robust code means code that doesn't
> misbehave when faced with unforeseen circumstances; having a logic error is
> such one unforeseen circumstance. Nobody is perfect.
>
> > not introduce subtle bugs
>>
>> In general, you cannot protect form users planting bugs in their logic.
>>
>
> And I do not. Accessing a null pointer crashes. I however can mitigate the
> effects of their bugs, which is what I'm doing.
>
> > and security vulnerabilities by allowing them scribble all over the >
>> stack.
>>
>> So, is this your goal? In case the programmer does not know what he is
>> doing (that he is using the library incorrectly), the library itself should
>> take actions to minimize the damage? I am somewhat convinced by that.
>>
>
> Yes, exactly.
>
> Here you have a union of an arbitrary type and std::error_code.
> std::error_code contains a pointer to a type with a vtable. Allowing the
> ability to write arbitrary data over that pointer is a security concern
> because this could lead to arbitrary code execution.
>
> > (Note that with the above spec, you can still assert in op* and it would
>> > be conforming,
>>
>> Do you mean that you are ok with operator* having a narrow contract?
>>
>
> It already has. I have deliberately left it implicit though.
>
> And we should also bare in mind Niall's opinion, that if he agrees to a
>> narrow contract, such function should spell longer than `value()`.
>>
>
> Yes, there is that.
>
> > The root of our disagreement is the idea that undefined behavior is good
>> > because it supposedly allows you to do this or that.
>>
>> It is not in this that I see the root of our disagreement. In fact, I do
>> not yet see where this root is. What I fight for is not an UB but narrow
>> contracts.
>>
>
> Traditionally the two are synonyms in C++. If you have a narrow contract,
> the behavior on contract violation is undefined. Can't have one without the
> other.

Maybe this calls for a new kind of precondition specification: that calling
a function in given circumstances is formally incorrect (tools are allowed
to make use of this information), but the component still guarantees a
rescue action.

Regards,
&rzej;


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk