digitalmars.D - Instantiation of nested structs should be allowed outside their parent
- Stanislav Blinov (15/15) Nov 07 2021 Just make it `@system`, and even then ONLY if something is
- Stefan Koch (5/20) Nov 08 2021 How would you want a struct to behave when it is used outside of
- Stanislav Blinov (5/8) Nov 08 2021 Throw? No, segfault like any other null dereference. Ain't no
- Alexandru Ermicioi (5/9) Nov 09 2021 I'd rather have compiler throw errors rather than having yet
- Stanislav Blinov (7/10) Nov 09 2021 In that case compilation of 99% of D programs on Unix has to fail
- Alexandru Ermicioi (18/28) Nov 09 2021 If compiler can help in preventing potential bugs, then it should
- Stanislav Blinov (15/24) Nov 09 2021 That's just the thing. That error message is, plain and simple,
- Alexandru Ermicioi (33/49) Nov 09 2021 .init field itself has lots of edge cases that are not well
- Alexandru Ermicioi (7/10) Nov 09 2021 You may also try iterate over all constructor overloads, and
- Stanislav Blinov (23/31) Nov 09 2021 No. .init of a nested struct just contains a null pointer in the
- Stanislav Blinov (4/5) Nov 08 2021 On second thought, doesn't even need to be @system. Just needs to
- Timon Gehr (8/14) Nov 09 2021 There is never really a need to instantiate a value of any type just to
- Stanislav Blinov (13/26) Nov 09 2021 Yes, there is. isInputRange tries, and fails for some nested
- Timon Gehr (11/27) Nov 09 2021 Fair, sometimes you want to check whether you can declare a value of the...
- Timon Gehr (3/8) Nov 09 2021 Well, that checks the destructor too of course. x)
- Stanislav Blinov (34/55) Nov 09 2021 The point is it must be possible to test that, and it's not,
- Timon Gehr (7/20) Nov 09 2021 template isNothrowCopyable(To, From)
- Timon Gehr (3/24) Nov 09 2021 (I don't know if that solves your problem, as I don't actually have an
- Stanislav Blinov (24/33) Nov 09 2021 ```d
- Timon Gehr (5/43) Nov 09 2021 It indeed seems ridiculous that you can't copy a nested struct using its...
- Stanislav Blinov (9/14) Nov 09 2021 Thing is you're not calling it with null. AFAIK, the compiler
- Timon Gehr (5/16) Nov 09 2021 Well, that's what it should do, but it seems what actually happens is
- Stanislav Blinov (3/4) Nov 09 2021 Oh and, of course, this type of compile-time tests aren't
- Timon Gehr (2/8) Nov 09 2021 Presumably you are testing because you want to actually do it later.
- Stanislav Blinov (11/20) Nov 09 2021 No, I am testing because I want to copy things :) Or have ranges
- Stanislav Blinov (3/4) Nov 09 2021 Should've said "closures", but whatever.
- Timon Gehr (5/9) Nov 09 2021 Sure, which is what I said (do it = copy things). But why would you want...
- Stanislav Blinov (14/17) Nov 09 2021 ? Because the check only needs to tell me if copying throws or
- Timon Gehr (10/28) Nov 09 2021 For the specific issue we have been discussing, it seems to me that
- Stanislav Blinov (76/103) Nov 09 2021 It does work just fine. Can copy NestedThatFails within its
- Timon Gehr (13/63) Nov 09 2021 Also, you won't be able to use it in `@safe` code. That's not the same
- Stanislav Blinov (71/110) Nov 09 2021 You can use it in @safe code. If you can determine that the copy
- Timon Gehr (7/9) Nov 09 2021 It will help in some cases. What will help with the specific issue you
- Stanislav Blinov (9/19) Nov 09 2021 Yes, it does :) But I think instead of "fixing" that error
- Timon Gehr (10/12) Nov 09 2021 You can get the "null" instance:
Just make it ` system`, and even then ONLY if something is actually getting instantiated and it's not just a CTFE test. Unless I'm missing something, that should be the behavior. The 'cannot access frame pointer' blah SO needs to go away... Rationale: instantiating things is super useful for compile time tests and inference. Nested structs complicate that to wazoo with that failure. If you're not convinced, I encourage you to try and define e.g. ```d enum bool isNothrowCopyable(To, From) = /* ??? */ ``` without reimplementing copy initialization by hand. It should be a trivial test. Just test copy-initialization of a union field. But no. Not when nested structs are involved. And they can get involved indirectly.
Nov 07 2021
On Monday, 8 November 2021 at 01:02:11 UTC, Stanislav Blinov wrote:Just make it ` system`, and even then ONLY if something is actually getting instantiated and it's not just a CTFE test. Unless I'm missing something, that should be the behavior. The 'cannot access frame pointer' blah SO needs to go away... Rationale: instantiating things is super useful for compile time tests and inference. Nested structs complicate that to wazoo with that failure. If you're not convinced, I encourage you to try and define e.g. ```d enum bool isNothrowCopyable(To, From) = /* ??? */ ``` without reimplementing copy initialization by hand. It should be a trivial test. Just test copy-initialization of a union field. But no. Not when nested structs are involved. And they can get involved indirectly.How would you want a struct to behave when it is used outside of it's context? Set the context pointer to null and throw upon access?
Nov 08 2021
On Tuesday, 9 November 2021 at 01:06:16 UTC, Stefan Koch wrote:How would you want a struct to behave when it is used outside of it's context? Set the context pointer to null and throw upon access?Throw? No, segfault like any other null dereference. Ain't no harm in that. As compared to absolutely ruining generic code for, frankly, no benefit, and numerous, NUMEROUS bugs and "workarounds" that simply don't need to exist.
Nov 08 2021
On Tuesday, 9 November 2021 at 01:54:44 UTC, Stanislav Blinov wrote:Throw? No, segfault like any other null dereference. Ain't no harm in that. As compared to absolutely ruining generic code for, frankly, no benefit, and numerous, NUMEROUS bugs and "workarounds" that simply don't need to exist.I'd rather have compiler throw errors rather than having yet another potential segfault in application which needs to be checked.
Nov 09 2021
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?
Nov 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: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, AlexandruI'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?
Nov 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.
Nov 09 2021
On Tuesday, 9 November 2021 at 19:00:38 UTC, Stanislav Blinov wrote:On Tuesday, 9 November 2021 at 18:35:44 UTC, Alexandru Ermicioi wrote:.init field itself has lots of edge cases that are not well thought, and you might just be hitting one of them. Per my understanding (which is not necessarily right, please correct if it), the .init of a nested struct should have the context pointer of the method it is called in setup. I.e. .init for such structs would be possible to use only in the scope of parent, be it class, or function body.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!Please, this is not helpful. There are already reports on this, including ones filed by myself. Why do you think I created this topic?Wasn't aware that they were there already, hence recommended to be filed.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.I was referring to nested structs. Inner or nested structs are the same thing, they are inside or nested into something. The only current solution I'd see to avoid this issue, would be to first static check if the type is nested type, and just use hasMember trait to check that a `__ctor` with one argument of same type as the structure itself is present. If so, most probably it is a copy constructor, and hence you can return true. I.e. check the signature of constructor, instead of actually trying to instantiate the nested struct inside is expression or `__traits` compiles expression. That should work even with disabled copy constructors, per my knowledge. I know, that sometimes D meta-programming is pain in some ... place due to bugs, and not well rounded edge cases, but the solution you proposed to fix that (have context pointer null), is a thing which is not better than the current language spec, in my opinion. So for now best is to try to convince people working on compiler that the issue is quite serious and needs working on (guess this thread itself), while in meantime, you may try use the solution I've proposed to do the checks. Best regards, Alexandru.
Nov 09 2021
On Tuesday, 9 November 2021 at 22:22:20 UTC, Alexandru Ermicioi wrote:... Best regards, Alexandru.You may also try iterate over all constructor overloads, and check for function signature with is expression. This should not force the compiler to instantiate the struct (and hence complain for missing context pointer), and achieve what you need in case for copy constructor.
Nov 09 2021
On Tuesday, 9 November 2021 at 22:22:20 UTC, Alexandru Ermicioi wrote:if it), the .init of a nested struct should have the context pointer of the method it is called in setup.No. .init of a nested struct just contains a null pointer in the .tupleof[$-1]. Context does not exist until you instantiate a nested struct within the parent scope. That's when an actual stack frame gets allocated. Consequently, doing things like T a = T.init; when T is a nested struct is exactly the kind of "bug" you're referring to. And the compiler is perfectly happy with that. Because, actually, that is not a bug at all. Just like leaving raw pointers null-initialized.The only current solution I'd see to avoid this issue, would be to first static check if the type is nested type, and just use hasMember trait to check that a `__ctor` with one argument of same type as the structure itself is present.There is a dedicated trait that shows presence of copy constructor. BTW, hasMember is the WORST method of checking for functions. Because of overloads, and because of templates. IMHO, it's only good for checking for the presence of a destructor, and that's about it. Please understand, I am not asking "how to work around this" here. I know how, I've already shown how. That is not what I want to be doing. I want the opposite - to not have to be doing that.If so, most probably it is a copy constructor, and hence you can return true.No, you can't. The ctor might be templated, so won't even exist unless you try to instantiate it. Or there might not be any copy constructor at all. Nor postblit. But the thing may simply not be copyable (i.e. const pointer into a pointer), hence the trait must return false.
Nov 09 2021
On Monday, 8 November 2021 at 01:02:11 UTC, Stanislav Blinov wrote:Just make it ` system`...On second thought, doesn't even need to be system. Just needs to be allowed.
Nov 08 2021
On 08.11.21 02:02, Stanislav Blinov wrote:If you're not convinced, I encourage you to try and define e.g. ```d enum bool isNothrowCopyable(To, From) = /* ??? */ ```There is never really a need to instantiate a value of any type just to check what it supports. Just use parameters in your function literal. This has the side effect of supporting `inout` shenanigans as well. enum bool isNothrowCopyable(To, From) = __traits(compiles, (To to, From from)nothrow{ to=from; }); (Untested. If I have misunderstood what it should check for, just change the function body.)
Nov 09 2021
On Tuesday, 9 November 2021 at 09:24:29 UTC, Timon Gehr wrote:On 08.11.21 02:02, Stanislav Blinov wrote:...WITHOUT REIMPLEMENTING COPY INITIALIZATION BY HAND.If you're not convinced, I encourage you to try and define e.g. ```d enum bool isNothrowCopyable(To, From) = /* ??? */ ```There is never really a need to instantiate a value of any type just to check what it supports.Yes, there is. isInputRange tries, and fails for some nested structs (keyword - some). This copy attributes check needs an instance too.enum bool isNothrowCopyable(To, From) = __traits(compiles, (To to, From from)nothrow{ to=from; });Nope. That tests assignment. Which may, or may not, also test destructor, depending on whether opAssign is defined. The check must needs to test ONLY copy-initialization (blit, copy ctor, and/or postblit).(Untested. If I have misunderstood what it should check for, just change the function body.)Yes. "Just". For that specific check - by recreating the copy semantics. I can show you the code I came up with, but like I said, I really do encourage you to try. Just to experience, in full, how ridiculous it gets.
Nov 09 2021
On 09.11.21 15:11, Stanislav Blinov wrote:On Tuesday, 9 November 2021 at 09:24:29 UTC, Timon Gehr wrote:Fair, sometimes you want to check whether you can declare a value of the given type, though that _should_ fail in case it's not actually possible for the algorithm to do that and get an actually working instance. It's plausible that that check does not actually work correctly for some Phobos functions though, as local instantiation of templates is a bit messy.... There is never really a need to instantiate a value of any type just to check what it supports.Yes, there is. isInputRange tries, and fails for some nested structs (keyword - some).This copy attributes check needs an instance too. >> enum bool isNothrowCopyable(To, From) = __traits(compiles, (To to,I see, I guess you actually want this, but it does not work: enum bool isNothrowCopyable(To, From) = __traits(compiles, (From from)nothrow{ To to=from; }); What's the use case for checking only this kind of initialization, if you don't need to create new instances of `to`?From from)nothrow{ to=from; });Nope. That tests assignment. Which may, or may not, also test destructor, depending on whether opAssign is defined. The check must needs to test ONLY copy-initialization (blit, copy ctor, and/or postblit). ...
Nov 09 2021
On 09.11.21 16:07, Timon Gehr wrote:I see, I guess you actually want this, but it does not work: enum bool isNothrowCopyable(To, From) = __traits(compiles, (From from)nothrow{ To to=from; });Well, that checks the destructor too of course. x) But now I understand why you want to instantiate To.
Nov 09 2021
On Tuesday, 9 November 2021 at 15:07:58 UTC, Timon Gehr wrote:On 09.11.21 15:11, Stanislav Blinov wrote:The point is it must be possible to test that, and it's not, because of some arbitrary artificial limitation. I mean, we can freely instantiate null delegates, but we can't instantiate nested structs? There's, to me, zero logic in that.On Tuesday, 9 November 2021 at 09:24:29 UTC, Timon Gehr wrote:Fair, sometimes you want to check whether you can declare a value of the given type, though that _should_ fail in case it's not actually possible for the algorithm to do that and get an actually working instance. It's plausible that that check does not actually work correctly for some Phobos functions though, as local instantiation of templates is a bit messy.... There is never really a need to instantiate a value of any type just to check what it supports.Yes, there is. isInputRange tries, and fails for some nested structs (keyword - some).I see, I guess you actually want this, but it does not work: enum bool isNothrowCopyable(To, From) = __traits(compiles, (From from)nothrow{ To to=from; });No, that also tests destruction, of both `from`, because it's D, where functions destruct their arguments, and `to`, because it's a local :) What I want is this: ```d template isNothrowCopyable(To, From) if (is(immutable To == immutable From)) { enum bool isNothrowCopyable = is(typeof((ref scope From from) nothrow { union U { To to; } U u = U(from); })); } ``` ...but it indeed doesn't work for nested structs. And a thing that does "work" has to reimplement compiler's copy semantics, including field-by-field blitting, calling constructors and postblits, etc.What's the use case for checking only this kind of initialization, if you don't need to create new instances of `to`?? You *do* need to create new instances of `to`. By copying existing instances. Any container that wants to support copying has to do that, and beyond. I want a test that would also tell me if my container needs special handling of exception guarantees, or if it can freely do an easy thing and not worry about it: can I just copy existing things on element insertion one element over, or do I have to do a three step algorithm because copying might throw?.. I also want other tests, that would tell me if copying is safe to do, if it needs GC, if it's pure... (I *do* have them already, but like I said, they're ludicrous, for no other reason than this handwaving error message). And as for isInputRange - I haven't got a clue if it's even possible to make it work for all nested structs unless the compiler stops issuing this error message.
Nov 09 2021
On 09.11.21 16:30, Stanislav Blinov wrote:```d template isNothrowCopyable(To, From) if (is(immutable To == immutable From)) { enum bool isNothrowCopyable = is(typeof((ref scope From from) nothrow { union U { To to; } U u = U(from); })); } ``` ...but it indeed doesn't work for nested structs. And a thing that does "work" has to reimplement compiler's copy semantics, including field-by-field blitting, calling constructors and postblits, etc.template isNothrowCopyable(To, From) if (is(immutable To == immutable From)) { enum bool isNothrowCopyable = is(typeof((ref scope From from) nothrow { union U { auto to=To.init; } U u = U(from); })); }
Nov 09 2021
On 09.11.21 16:36, Timon Gehr wrote:On 09.11.21 16:30, Stanislav Blinov wrote:(I don't know if that solves your problem, as I don't actually have an example where your implementation does not work.)```d template isNothrowCopyable(To, From) if (is(immutable To == immutable From)) { enum bool isNothrowCopyable = is(typeof((ref scope From from) nothrow { union U { To to; } U u = U(from); })); } ``` ...but it indeed doesn't work for nested structs. And a thing that does "work" has to reimplement compiler's copy semantics, including field-by-field blitting, calling constructors and postblits, etc.template isNothrowCopyable(To, From) if (is(immutable To == immutable From)) { enum bool isNothrowCopyable = is(typeof((ref scope From from) nothrow { union U { auto to=To.init; } U u = U(from); })); }
Nov 09 2021
On Tuesday, 9 November 2021 at 15:39:48 UTC, Timon Gehr wrote:```d template isNothrowCopyable(To, From = To) if (is(immutable To == immutable From)) { enum bool isNothrowCopyable = is(typeof((ref scope From from) nothrow { union U { auto to=To.init; } U u = U(from); })); } unittest { int dtors; struct NestedThatPasses { ~this() { ++dtors; } } struct NestedThatFails { this(return ref scope typeof(this)) nothrow {} ~this() { ++dtors; } } static assert(isNothrowCopyable!NestedThatPasses); static assert(isNothrowCopyable!NestedThatFails); } ```template isNothrowCopyable(To, From) if (is(immutable To == immutable From)) { enum bool isNothrowCopyable = is(typeof((ref scope From from) nothrow { union U { auto to=To.init; } U u = U(from); })); }(I don't know if that solves your problem, as I don't actually have an example where your implementation does not work.)
Nov 09 2021
On 09.11.21 16:44, Stanislav Blinov wrote:On Tuesday, 9 November 2021 at 15:39:48 UTC, Timon Gehr wrote:It indeed seems ridiculous that you can't copy a nested struct using its copy constructor. I guess one solution would be to use the frame pointer of the instance you are about to copy to call the copy constructor. I don't think calling it with `null` is a great idea.```d template isNothrowCopyable(To, From = To) if (is(immutable To == immutable From)) { enum bool isNothrowCopyable = is(typeof((ref scope From from) nothrow { union U { auto to=To.init; } U u = U(from); })); } unittest { int dtors; struct NestedThatPasses { ~this() { ++dtors; } } struct NestedThatFails { this(return ref scope typeof(this)) nothrow {} ~this() { ++dtors; } } static assert(isNothrowCopyable!NestedThatPasses); static assert(isNothrowCopyable!NestedThatFails); } ```template isNothrowCopyable(To, From) if (is(immutable To == immutable From)) { enum bool isNothrowCopyable = is(typeof((ref scope From from) nothrow { union U { auto to=To.init; } U u = U(from); })); }(I don't know if that solves your problem, as I don't actually have an example where your implementation does not work.)
Nov 09 2021
On Tuesday, 9 November 2021 at 15:55:14 UTC, Timon Gehr wrote:It indeed seems ridiculous that you can't copy a nested struct using its copy constructor. I guess one solution would be to use the frame pointer of the instance you are about to copy to call the copy constructor. I don't think calling it with `null` is a great idea.Thing is you're not calling it with null. AFAIK, the compiler blits the context first, then calls the ctor. Pretty much the only way to crash with nested structs is to either use a default-initialized value, or do some very unsafe things. And even then - only if you actually call methods that do access the context. Which not all of them have to do. ...and then we get to even more "interesting" unittests, where a struct contains an instance of a nested struct...
Nov 09 2021
On 09.11.21 16:59, Stanislav Blinov wrote:On Tuesday, 9 November 2021 at 15:55:14 UTC, Timon Gehr wrote:Well, that's what it should do, but it seems what actually happens is that compilation fails instead because the compiler thinks it needs a context from somewhere else to create a new instance. This seems like a bug to me.It indeed seems ridiculous that you can't copy a nested struct using its copy constructor. I guess one solution would be to use the frame pointer of the instance you are about to copy to call the copy constructor. I don't think calling it with `null` is a great idea.Thing is you're not calling it with null. AFAIK, the compiler blits the context first, then calls the ctor.
Nov 09 2021
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.
Nov 09 2021
On 09.11.21 17:04, Stanislav Blinov wrote:On Tuesday, 9 November 2021 at 15:55:14 UTC, Timon Gehr wrote:Presumably you are testing because you want to actually do it later.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.
Nov 09 2021
On Tuesday, 9 November 2021 at 16:06:02 UTC, Timon Gehr wrote:On 09.11.21 17:04, Stanislav Blinov wrote: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.On Tuesday, 9 November 2021 at 15:55:14 UTC, Timon Gehr wrote:Presumably you are testing because you want to actually do it later.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.
Nov 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.
Nov 09 2021
On 09.11.21 17:19, Stanislav Blinov 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? (As I have argued in the other post, both the check and actual code should just succeed.)Presumably you are testing because you want to actually do it later.No, I am testing because I want to copy things :)
Nov 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.
Nov 09 2021
On 09.11.21 17:51, Stanislav Blinov wrote:On Tuesday, 9 November 2021 at 16:34:26 UTC, Timon Gehr wrote: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.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. ...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.
Nov 09 2021
On Tuesday, 9 November 2021 at 17:18:31 UTC, Timon Gehr wrote:On 09.11.21 17:51, Stanislav Blinov wrote: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 ;)On Tuesday, 9 November 2021 at 16:34:26 UTC, Timon Gehr wrote: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.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.I don't understand what you're asking. What's different where?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?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); } ```
Nov 09 2021
On 09.11.21 19:41, Stanislav Blinov wrote:On Tuesday, 9 November 2021 at 17:18:31 UTC, Timon Gehr wrote: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.On 09.11.21 17:51, Stanislav Blinov wrote: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, andOn Tuesday, 9 November 2021 at 16:34:26 UTC, Timon Gehr wrote: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.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.2) look at its source code, the very first static if inside ;)You said: "should be _that same_ union test [...] it is not". My question was what is not the same.I don't understand what you're asking. What's different where? ...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?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.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.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.
Nov 09 2021
On Tuesday, 9 November 2021 at 21:08:08 UTC, Timon Gehr wrote:On 09.11.21 19:41, Stanislav Blinov wrote: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.On Tuesday, 9 November 2021 at 17:18:31 UTC, Timon Gehr wrote: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.On 09.11.21 17:51, Stanislav Blinov wrote: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, andOn Tuesday, 9 November 2021 at 16:34:26 UTC, Timon Gehr wrote: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.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.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.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! :DJust to reiterate - I *have* a working implementation of a test that works around this problem for testing copy initialization specifically.
Nov 09 2021
On 09.11.21 22:45, Stanislav Blinov wrote:That won't help.It will help in some cases. What will help with the specific issue you have shown was already explained in the first part of my post. I.e., the compiler needs fixing. https://issues.dlang.org/show_bug.cgi?id=22499 Of course, this was just one example. Again: I am pretty sure that if you explain other pain points we will find more bugs in the compiler.
Nov 09 2021
On Tuesday, 9 November 2021 at 22:20:35 UTC, Timon Gehr wrote:On 09.11.21 22:45, Stanislav Blinov wrote:Yes, it does :) But I think instead of "fixing" that error message it simply needs to be thrown out. It's not doing anything useful, and plenty harmful.That won't help.It will help in some cases. What will help with the specific issue you have shown was already explained in the first part of my post. I.e., the compiler needs fixing.https://issues.dlang.org/show_bug.cgi?id=22499 Of course, this was just one example. Again: I am pretty sure that if you explain other pain points we will find more bugs in the compiler.I believe that would be the same as https://issues.dlang.org/show_bug.cgi?id=22446 There are also some other nested-related things. https://issues.dlang.org/show_bug.cgi?id=22434 https://issues.dlang.org/show_bug.cgi?id=19375
Nov 09 2021
On 09.11.21 16:30, Stanislav Blinov wrote:I mean, we can freely instantiate null delegates, but we can't instantiate nested structs?You can get the "null" instance: auto foo(int x){ struct S{ int t(){ return x; } } return S(); } void main(){ // typeof(foo(0)) s; // error auto s=typeof(foo(0)).init; // ok }
Nov 09 2021