Thread overview
How to workaround on this (bug?)
Sep 16, 2022
frame
Sep 16, 2022
H. S. Teoh
Sep 17, 2022
frame
Sep 17, 2022
H. S. Teoh
Sep 23, 2022
Quirin Schroll
Sep 23, 2022
Kagamin
September 16, 2022
import std.variant;

// error: destructor `std.variant.VariantN!32LU.VariantN.~this` is not `nothrow`
void fun(Variant v) nothrow
{

}

void main()
{
   fun(Variant());
}

A reference, pointer or slice works. I could do something on the caller site but the signature of fun() should remain like above.

September 16, 2022
On Fri, Sep 16, 2022 at 10:43:43PM +0000, frame via Digitalmars-d-learn wrote:
> ```d
> import std.variant;
> 
> // error: destructor `std.variant.VariantN!32LU.VariantN.~this` is not
> `nothrow`
> void fun(Variant v) nothrow
> {
> 
> }
> 
> void main()
> {
>    fun(Variant());
> }
> ```
> 
> A reference, pointer or slice works.

Yes, because in that case the dtor would not need to be invoked in .fun.


> I could do something on the caller site but the signature of `fun()`
> should remain like above.

I was doing to suggest using `const ref`, but if the signature must remain the same, then I'm out of ideas.

Basically, if you pass something to .fun by value, then that value must be destroyed by .fun once it's ready to return.  So if the value has a dtor, the dtor must be called upon exiting from .fun.  Since Variant has a throwing dtor, this means .fun may throw when it's about to return, which violates `nothrow`.


T

-- 
If creativity is stifled by rigid discipline, then it is not true creativity.
September 17, 2022

On Friday, 16 September 2022 at 23:06:35 UTC, H. S. Teoh wrote:

>

Basically, if you pass something to .fun by value, then that value must be destroyed by .fun once it's ready to return. So if the value has a dtor, the dtor must be called upon exiting from .fun. Since Variant has a throwing dtor, this means .fun may throw when it's about to return, which violates nothrow.

I understand why this happens but not why the compiler does not check if the value is actually destroyed in user code by .destroy().

Thanks for your suggestion, I will consider this.

September 16, 2022
On Sat, Sep 17, 2022 at 12:19:16AM +0000, frame via Digitalmars-d-learn wrote:
> On Friday, 16 September 2022 at 23:06:35 UTC, H. S. Teoh wrote:
> 
> > Basically, if you pass something to .fun by value, then that value must be destroyed by .fun once it's ready to return.  So if the value has a dtor, the dtor must be called upon exiting from .fun. Since Variant has a throwing dtor, this means .fun may throw when it's about to return, which violates `nothrow`.
> 
> I understand why this happens but not why the compiler does not check if the value is actually destroyed in user code by `.destroy()`.
[...]

I don't know for sure, but my guess is that .destroy is seen by the compiler just like any other D function; i.e., it's not treated specially. So it would not factor into the compiler's lifetime considerations.

Or are you talking about the case where the Variant may not actually contain values that have dtors?  That information is not available until runtime; the compiler can't make decisions based on that.


T

-- 
Political correctness: socially-sanctioned hypocrisy.
September 23, 2022

On Friday, 16 September 2022 at 22:43:43 UTC, frame wrote:

>
import std.variant;

// error: destructor `std.variant.VariantN!32LU.VariantN.~this` is not `nothrow`
void fun(Variant v) nothrow
{

}

void main()
{
   fun(Variant());
}

A reference, pointer or slice works. I could do something on the caller site but the signature of fun() should remain like above.

A reference effectively is a never-null pointer. A slice is a pointer to the first of many objects plus the number of those objects (or empty, or null).
It boils down to pointers, and the pointed-to Variant object is not the responsibility of fun.

When you have a parameter that binds by copy, you cannot escape from calling its destructor, and if one happens not to be nothrow, your function cannot be nothrow.

The new semantics for in (compile with -preview=in) might work for you. The in storage class binds by copy if the copy is cheap – which I suspect is never the case for a Variant – or else by reference; and it can bind temporaries by reference (unlike ref). However, in also incurs const and scope. It is unlikely that scope will be your problem, but const very well might be an issue when the contained value has indirections to mutable values, e.g. an int[] will be read as a const(int)[].

Calling the destructor is then the responsibility of the caller.

// Compile with -preview=in

import std.variant;

void fun(in Variant v) nothrow { }

void main()
{
    fun(Variant()); // okay: `in` binds rvalues
    Variant v;
    fun(v); // okay: `in` binds lvalues
}
September 23, 2022

Provide two functions and let the caller choose

void fun(ref Variant v) nothrow
{
}

void fun2(Variant v)
{
	fun(v);
}