November 09, 2021
On Tuesday, 9 November 2021 at 16:06:02 UTC, Timon Gehr wrote:
> On 09.11.21 17:04, Stanislav Blinov wrote:
>> On Tuesday, 9 November 2021 at 15:55:14 UTC, Timon Gehr wrote:
>> 
>>> I don't think calling it with `null` is a great idea.
>> 
>> Oh and, of course, this type of compile-time tests aren't actually calling anything at all anyway.
>
> Presumably you are testing because you want to actually do it later.

No, I am testing because I want to copy things :) Or have ranges of them. The NestedThatFails from before. Array of that is not an input range, according to Phobos. Because it fails one of the checks. Because the compiler is being overly cautious.

Which, again, makes NO sense. Nested struct is, effectively, just a family of delegates packed together in one type. There's zero reason not to let it be instantiated in some specific cases. Or then we have to forbid instantiation of ALL delegates unless we're providing context. Thus making delegates at least 50% useless.
November 09, 2021
On Tuesday, 9 November 2021 at 16:19:04 UTC, Stanislav Blinov wrote:

> just a family of delegates packed together in one type.

Should've said "closures", but whatever.
November 09, 2021
On 09.11.21 17:19, Stanislav Blinov wrote:
>>
>> Presumably you are testing because you want to actually do it later.
> 
> No, I am testing because I want to copy things :)

Sure, which is what I said (do it = copy things). But why would you want the check to behave differently from actual code that copies things? (As I have argued in the other post, both the check and actual code should just succeed.)
November 09, 2021
On Tuesday, 9 November 2021 at 16:34:26 UTC, Timon Gehr wrote:

> Sure, which is what I said (do it = copy things). But why would you want the check to behave differently from actual code that copies things?

? Because the check only needs to tell me if copying throws or not. That specific check, that is. Nothing else. Because that information drives how I allocate or how, exactly, do I copy things. Other checks drive other things. BTW, I forgot to show one more thing, that is that isNothrowCopyable reduces to isCopyable for BetterC. Not Phobos' isCopyable, but an isCopyable that tests distinct types because qualifier hell. Which should be that same union test from before, only without attributes. But, alas, it is not.

For nested structs, such checks need to do things outside of context - that's why they MAY behave differently from actual code. Compiler doesn't like that. I think it shouldn't not like that.
November 09, 2021
On 09.11.21 17:51, Stanislav Blinov wrote:
> On Tuesday, 9 November 2021 at 16:34:26 UTC, Timon Gehr wrote:
> 
>> Sure, which is what I said (do it = copy things). But why would you want the check to behave differently from actual code that copies things?
> 
> ? Because the check only needs to tell me if copying throws or not.

For the specific issue we have been discussing, it seems to me that copying does not work at all, independent of whether it's throwing or not. It should just work.

> That specific check, that is. Nothing else. Because that information drives how I allocate or how, exactly, do I copy things. Other checks drive other things. BTW, I forgot to show one more thing, that is that isNothrowCopyable reduces to isCopyable for BetterC. Not Phobos' isCopyable, but an isCopyable that tests distinct types because qualifier hell. Which should be that same union test from before, only without attributes. But, alas, it is not.
> ...

What's different?

> For nested structs, such checks need to do things outside of context - that's why they MAY behave differently from actual code. Compiler doesn't like that. I think it shouldn't not like that.

Why should the checks be able to do anything that standard code cannot do? Why should the checks not have access to context when the actual code will have such access? I think a lot of the trouble you are having probably comes down to compiler bugs and/or missing reasonable enhancement requests.
November 09, 2021

On Tuesday, 9 November 2021 at 15:39:49 UTC, Stanislav Blinov wrote:

>

On Tuesday, 9 November 2021 at 12:59:08 UTC, Alexandru Ermicioi wrote:

>

I'd rather have compiler throw errors rather than having yet another potential segfault in application which needs to be checked.

In that case compilation of 99% of D programs on Unix has to fail immediately. Because, potentially, they can all segfault before even reaching _Dmain. Only those that don't in any way link to C runtime may compile freely.

Sound silly enough?

If compiler can help in preventing potential bugs, then it should do that, given it is in scope of what it handles. C runtime is not handled by it, and therefore it can only make assumptions that is working correctly just like any other already compiled library that is being used by the source code that is compiled right now.

The problem from discussion in this thread seems to be due to bugs in how copy constructor is implemented, therefore first thing would be best to do, is to file a bug report. After this you can try fix it yourself, wait for other person do it, or motivate people in doing it.

Imho this is not a minor thing, and should be fixed quite fast. In meantime, you could try make inner struct static. This should eliminate the context pointer.

Best regards,
Alexandru

November 09, 2021
On Tuesday, 9 November 2021 at 17:18:31 UTC, Timon Gehr wrote:
> On 09.11.21 17:51, Stanislav Blinov wrote:
>> On Tuesday, 9 November 2021 at 16:34:26 UTC, Timon Gehr wrote:
>> 
>>> Sure, which is what I said (do it = copy things). But why would you want the check to behave differently from actual code that copies things?
>> 
>> ? Because the check only needs to tell me if copying throws or not.
>
> For the specific issue we have been discussing, it seems to me that copying does not work at all, independent of whether it's throwing or not. It should just work.

It does work just fine. Can copy NestedThatFails within its context. Can copy it to outside of its context too if need be, with copyEmplace. Before you ask "why not just use copyEmplace for the test then?" the answers are 2:

1) it's '@system', so I won't be able to devise an isSafe... test out of it, and
2) look at its source code, the very first static if inside ;)

>> That specific check, that is. Nothing else. Because that information drives how I allocate or how, exactly, do I copy things. Other checks drive other things. BTW, I forgot to show one more thing, that is that isNothrowCopyable reduces to isCopyable for BetterC. Not Phobos' isCopyable, but an isCopyable that tests distinct types because qualifier hell. Which should be that same union test from before, only without attributes. But, alas, it is not.
>> ...
>
> What's different?

I don't understand what you're asking. What's different where?

> Why should the checks be able to do anything that standard code cannot do? Why should the checks not have access to context when the actual code will have such access? I think a lot of the trouble you are having probably comes down to compiler bugs and/or missing reasonable enhancement requests.

You've seen the test. It's a lambda outside of unittest. How do you propose to write a trait that DOES have access to context? Same goes for std.range.isInputRange...

Just to reiterate - I *have* a working implementation of a test that works around this problem for testing copy initialization specifically. I'm just tired of these workarounds.

```d
struct Container(T)
{
    import core.memory : pureMalloc, pureFree;
    T* storage;
    this(T x)
    {
        // Make known to GC, yadda yadda, skipped for brevity
        import core.lifetime : moveEmplace;
        storage = cast(T*) pureMalloc(T.sizeof);
        moveEmplace(x, *storage);
    }

    this(ref return scope typeof(this) other)
    {
        // This is where I'd use an isNothrowCopyable trait.
        // If T is nothrow-copyable, I'd just allocate and copy all things.
        // If it isn't, I'll need an allocation guard and a copy algorithm
        // that tracks success and destructs already copied things if a given copy fails.
        import core.lifetime : copyEmplace;
        storage = cast(T*) pureMalloc(T.sizeof);
        copyEmplace(*other.storage, *storage);
    }

    ~this() nothrow
    {
        if (storage)
        {
            static if (__traits(hasMember, T, "__xdtor"))
                storage.__xdtor;
            pureFree(storage);
        }
    }
}

unittest
{
    int dtors;
    struct NestedThatFails
    {
        this(return ref scope typeof(this)) nothrow {}
        ~this() nothrow { ++dtors; }
    }

    {
        NestedThatFails y;
        () nothrow {
            Container!NestedThatFails container = y; // copies just fine
            auto copied = container;                 // copies just fine
        } ();
    }

    // y, the copy passed to `container`, the value in `container`, the value in `copied`,
    // should be 4 dtors (well, ideally 3, but that's only after move semantics DIP is implemented).
    assert(dtors == 4);

    // But according to the check, it's not nothrow-copyable!
    static assert(isNothrowCopyable!NestedThatFails);
}

```
November 09, 2021

On Tuesday, 9 November 2021 at 18:35:44 UTC, Alexandru Ermicioi wrote:

>

If compiler can help in preventing potential bugs...

That's just the thing. That error message is, plain and simple, handwaving. Unless, like I said already, I'm missing something. The compiler is perfectly happy with me initializing a nested struct to its .init value. But instantiating it elsewhere - nuuuh, that's not allowed!

>

The problem from discussion in this thread seems to be due to bugs in how copy constructor is implemented, therefore first thing would be best to do, is to file a bug report. After this you can try fix it yourself, wait for other person do it, or motivate people in doing it.

Please, this is not helpful. There are already reports on this, including ones filed by myself. Why do you think I created this topic?

>

Imho this is not a minor thing, and should be fixed quite fast. In meantime, you could try make inner struct static. This should eliminate the context pointer.

That is not "inner" struct. That is nested struct. And the whole point of this topic is nested structs. I don't need to eliminate context pointer. I need generic code to work with nested structs. Including the copy tests being discussed here. Including, for gods sake, Phobos ranges.

November 09, 2021
On 09.11.21 19:41, Stanislav Blinov wrote:
> On Tuesday, 9 November 2021 at 17:18:31 UTC, Timon Gehr wrote:
>> On 09.11.21 17:51, Stanislav Blinov wrote:
>>> On Tuesday, 9 November 2021 at 16:34:26 UTC, Timon Gehr wrote:
>>>
>>>> Sure, which is what I said (do it = copy things). But why would you want the check to behave differently from actual code that copies things?
>>>
>>> ? Because the check only needs to tell me if copying throws or not.
>>
>> For the specific issue we have been discussing, it seems to me that copying does not work at all, independent of whether it's throwing or not. It should just work.
> 
> It does work just fine. Can copy NestedThatFails within its context. Can copy it to outside of its context too if need be, with copyEmplace. Before you ask "why not just use copyEmplace for the test then?" the answers are 2:
> 
> 1) it's '@system', so I won't be able to devise an isSafe... test out of it, and

Also, you won't be able to use it in `@safe` code. That's not the same as "just work". The compiler should just do this correctly, just like copyEmplace.

> 2) look at its source code, the very first static if inside ;)
> 
>>> That specific check, that is. Nothing else. Because that information drives how I allocate or how, exactly, do I copy things. Other checks drive other things. BTW, I forgot to show one more thing, that is that isNothrowCopyable reduces to isCopyable for BetterC. Not Phobos' isCopyable, but an isCopyable that tests distinct types because qualifier hell. Which should be that same union test from before, only without attributes. But, alas, it is not.
>>> ...
>>
>> What's different?
> 
> I don't understand what you're asking. What's different where?
> ...

You said: "should be _that same_ union test [...] it is not". My question was what is not the same.

>> Why should the checks be able to do anything that standard code cannot do? Why should the checks not have access to context when the actual code will have such access? I think a lot of the trouble you are having probably comes down to compiler bugs and/or missing reasonable enhancement requests.
> 
> You've seen the test. It's a lambda outside of unittest. How do you propose to write a trait that DOES have access to context? Same goes for std.range.isInputRange...
> ...

The constraint should have as much access to context as the function itself does. E.g., templates are sometimes instantiated locally in the caller's context.

> Just to reiterate - I *have* a working implementation of a test that works around this problem for testing copy initialization specifically.

Well, that specific problem comes down to what I think is just a compiler bug, I was just suggesting that probably there are other things that need to be fixed in the compiler as well.

> I'm just tired of these workarounds.
> ...

As you should be. It's not an acceptable situation.
November 09, 2021
On Tuesday, 9 November 2021 at 21:08:08 UTC, Timon Gehr wrote:
> On 09.11.21 19:41, Stanislav Blinov wrote:
>> On Tuesday, 9 November 2021 at 17:18:31 UTC, Timon Gehr wrote:
>>> On 09.11.21 17:51, Stanislav Blinov wrote:
>>>> On Tuesday, 9 November 2021 at 16:34:26 UTC, Timon Gehr wrote:
>>>>
>>>>> Sure, which is what I said (do it = copy things). But why would you want the check to behave differently from actual code that copies things?
>>>>
>>>> ? Because the check only needs to tell me if copying throws or not.
>>>
>>> For the specific issue we have been discussing, it seems to me that copying does not work at all, independent of whether it's throwing or not. It should just work.
>> 
>> It does work just fine. Can copy NestedThatFails within its context. Can copy it to outside of its context too if need be, with copyEmplace. Before you ask "why not just use copyEmplace for the test then?" the answers are 2:
>> 
>> 1) it's '@system', so I won't be able to devise an isSafe... test out of it, and
>
> Also, you won't be able to use it in `@safe` code. That's not the same as "just work". The compiler should just do this correctly, just like copyEmplace.

You can use it in @safe code. If you can determine that the copy is actually safe, and you're not corrupting anyone's memory. Just like the ctor from my example in the previous post.

> You said: "should be _that same_ union test [...] it is not". My question was what is not the same.

I meant that currently it can't be "that same union test only without the attributes" because it will also fail, i.e. it will evaluate to false for copyable structs, for the same reasons. So, alas, it has to be a workaround monstrosity.

>> You've seen the test. It's a lambda outside of unittest. How do you propose to write a trait that DOES have access to context? Same goes for std.range.isInputRange...
>> ...

> The constraint should have as much access to context as the function itself does. E.g., templates are sometimes instantiated locally in the caller's context.

That won't help. Look at the example from my previous post. Container is nowhere near that context, and can't be. But it needs to be able to get *useful* introspection out of T. It won't if the compiler bails artificially.

>> Just to reiterate - I *have* a working implementation of a test that works around this problem for testing copy initialization specifically.


For reference:

```d
enum bool isCopyable(T, From=T)     = is(typeof(inferCopyAttributesIfCopyable!(T,From)));
enum bool isSafeCopyable(T, From=T) = is(typeof(() @safe => inferCopyAttributesIfCopyable!(T,From)));
enum bool isNogcCopyable(T, From=T) = is(typeof(() @nogc => inferCopyAttributesIfCopyable!(T,From)));
version (D_BetterC) {
    enum bool isNothrowCopyable(T, From=T) = .isCopyable!(T, From);
} else {
    enum bool isNothrowCopyable(T, From=T) = is(typeof(() nothrow => inferCopyAttributesIfCopyable!(T,From)));
}

void inferIfBlittable(To, From)(scope To* to = null, scope From* from = null)
if (is(immutable To == immutable From))
{
    static if (__traits(isStaticArray, To))
        inferIfBlittable(&(*to)[0], &(*from)[0]);
    else static if (is(To == struct)) {
        foreach (i, ref it; to.tupleof[0 .. $-__traits(isNested, To)]) {
            inferIfBlittable(&it, &from.tupleof[i]);
        }
    } else {
        To copied = *from;
    }
}

void inferCopyAttributesIfCopyable(To, From)(scope To* to = null, scope From* from = null)
if (is(immutable To == immutable From))
{
    static if (__traits(isStaticArray, To))
        inferCopyAttributesIfCopyable(&(*to)[0], &(*from)[0]);
    else static if (is(To == struct)) {
        // This all SOOOO does not need to exist...
        // Unfortunately, if the struct is nested, doing a simple test
        // would result in "cannot access frame pointer" blah blah,
        // and if a nested struct is a field in some other struct, well,
        // that's even more "pleasant"...
        static if (__traits(hasPostblit, To)) {
            inferIfBlittable(to, from);
            to.__xpostblit;
        } else static if (__traits(hasCopyConstructor, To)) {
            to.__ctor(*from);
        } else {
            inferIfBlittable(to, from);
        }
    } else {
        To copied = *from;
    }
}
```

I mean, seriously, what the what! :D