Re: opDispatch and alias this
On Tuesday, 26 June 2018 at 00:56:13 UTC, Jonathan M Davis wrote: On Monday, June 25, 2018 23:13:12 Seb via Digitalmars-d wrote: - Jonathan M Davis I've tried to fix this issue. If briefly there is one collision: Final alias this should be r-value: static struct S { int i; } void test(ref S s) {} auto val = Final!S(42); assert (!__traits(compiles, test(val))); //val works as r-value However should be able to mutate fields of val: val.i = 24; assert(val.i == 24); //should works We may discard this case: for struct is value-type and head const should disable changing of fields. For example in C++ you can't declare instance of struct when you can not reassign instance, but can reassign a field.
Re: D is now catching C++ exceptions!
On Monday, 18 January 2016 at 22:26:56 UTC, Walter Bright wrote: at least for 64 bit Linux. Other platforms to follow. https://github.com/D-Programming-Language/dmd/pull/5342 This is what Andrei and I call "enabling" technology, as it opens the door for many more uses of D, in this case better interoperability with existing C++ codebases. Thanks to everyone who helped out with this, especially Elie, Iain, David and Andrei! Also looking forward to getting this in GDC and LDC! Andrei and I feel that better interoperability with C++ is a major strategic feature and advantage for D. As the recent thread with Manu's frustrations in doing it show, we still have a significant way to go. But I hope to push it forward hard in the next few months. For the fearless who love working under the hood, extending the support to the rest of the platforms is a great way to materially contribute. This is a good start and we should continue this work. We need review our documentation about interfacing to C++ and clearly delineate the limits of our features. For example we should say how do we may write D binding of C++ class. How do we may inherit D-defined C++ class from external C++ class/interface? When can we pass object of inherited class back to C++ code? How do we may cast one extern(C++) class to another extern(C++) class in D? In C++? Which C++ features we support for different ABIs? Also, I think, we need to implement linking to some C++ features (I'll help with this when I finish my alias this duty): Linking to C++ overloaded operators (without moving their semantic to D). C++ ctors/dtors. Dynamic casting of C++ objects in D. Maybe partial C++ RTTI support.
Re: extern(C++) multiple inheritence
On Wednesday, 20 January 2016 at 00:45:34 UTC, Walter Bright wrote: On 1/19/2016 1:59 PM, Daniel Murphy wrote: On 19/01/2016 8:04 PM, Walter Bright wrote: On 1/19/2016 12:34 AM, Daniel Murphy wrote: Yeah, it never has. No attempt has ever been made to make it work. Actually, as I recall it was made to match what DMC++ generates for Win32. Wasn't that from before we had extern(C++) classes? I did the extern(C++) single inheritance class layout but didn't touch interfaces. We had COM, which was an earlier form of C++ class support. BTW, in docs I saw the following example: Calling D Virtual Functions From C++ ``` Given D code like: extern (C++) int callE(E); extern (C++) interface E { int bar(int i, int j, int k); } class F : E { extern (C++) int bar(int i, int j, int k) { writefln("i = %s", i); writefln("j = %s", j); writefln("k = %s", k); return 8; } } void main() { F f = new F(); callE(f); } ``` ``` The C++ code to access it looks like: class E { public: virtual int bar(int i, int j, int k); }; int callE(E *e) { return e->bar(11,12,13); } ``` In this example C++ class E hasn't fields and another stuff. What happens if I change E? class E { std::string s; public: virtual int bar(int i, int j, int k); int strageHash() { return s.length() + bar(0, 0, 0); } }; int callE(E *e) { return e->strageHash(); } AFAIK, D can't generate correct F layout. D doesn't know sizeof(E), D doesn't know E ctor (the second one is fixable). As result, new F() object can be smaller that E and this code has an unexpected behaviour. Ok, we aren't gods and we can't suggest something better (exclusive of moveing multiple inheritance and C++ layout to D :). However we may and should alert user about this limitation and about another limitations like this. And when we add a new C++-related feature, we should declare C++-side limitations, in the first place, because they can't be enforced by D compiler. And have someone any ideas about C++ to D interface generator?
Re: Martin Nowak is officially MIA
On Thursday, 18 June 2015 at 02:54:00 UTC, Morbid.Obesity wrote: On Wednesday, 17 June 2015 at 16:16:09 UTC, berlin wrote: well, read something to your world situation. take it from an old kufr that dos not want to live under islamic law: http://www.jihadwatch.org/ http://www.thereligionofpeace.com/ http://www.barenakedislam.com/ http://schnellmann.org/Understanding_Muhammad_Contents.html BTW, I think, our troll just advertised his resources. It was the main his goal.
Re: Martin Nowak is officially MIA
On Thursday, 18 June 2015 at 02:22:13 UTC, Vladimir Panteleev wrote: On Wednesday, 17 June 2015 at 18:35:48 UTC, Andrei Alexandrescu wrote: With this we revoke Martin's role as release czar. His github access will remain the same for the time being. I think that was unnecessarily harsh and unilateral, not to mention demotivating (to both Martin and any people who considered the role). What do you mean by for the time being exactly? We are therefore looking for a new release czar. I would like to ask, what can we improve in our tooling and infrastructure to lessen the burden on release czars? I know nightly builds have been discussed for years, and it would be great to take advantage of the multi-platform infrastructure of the current autotester for it, but it doesn't look like that's going to happen. Sorry for this post but... Is it possible to remove disscusiion about relligions, nationalities and relations of them? This is international forum and we may allow only one kind of intolerance: we may hate programmers, which doesn't use D yet. I am surprised that disscussion, started by one troll passersby, doesn't stopped now. Camrades, do not answer to troll posts. Just ignore it. I do not understand how something can be to try to answer such posts dripping fat as that from which it started.
Re: Martin Nowak is officially MIA
On Wednesday, 17 June 2015 at 02:16:37 UTC, Andrei Alexandrescu wrote: Hello, Martin has not replied to any communication for more than two weeks now, and I'm starting to fear something might have happened to him. If anyone in Berlin could get in touch with him and let me/us know he's alright, I'd appreciate it. It's okay if he's bothered about missing his flight to DConf or anything related, but the perspective of a more serious problem is worrisome. We are therefore looking for a new release czar. Two would be even better to avoid similar problems in the future. Please let everybody know if interested. Thanks, Andrei He returned back to github and posted some messages. End alarm:)
Re: forum.dlang.org, version 2 (BETA)
On Friday, 5 June 2015 at 01:52:07 UTC, Vladimir Panteleev wrote: On Friday, 5 June 2015 at 01:48:20 UTC, Meta wrote: How feasible is it to add code formatting for the web interface? Not sure what you mean. Do you mean syntax highlighting for D code? If you mean the rewrapping issues with forum.dlang.org, those should be fixed now. Code (and other text with hard line breaks) should be sent and displayed as-is. Sorry if this question has been raised but is the reason for the inability to edit posts? Will be this feature implemented sometime?
Re: forum.dlang.org, version 2 (BETA)
On Friday, 5 June 2015 at 02:03:48 UTC, Vladimir Panteleev wrote: On Friday, 5 June 2015 at 02:02:11 UTC, IgorStepanov wrote: Sorry if this question has been raised but is the reason for the inability to edit posts? Posts are not editable because once sent, they are relayed to the NNTP server, mailing lists, and users' email inboxes. Will be this feature implemented sometime? Only when it becomes possible to edit a sent email. Ok, why we use email/NNTP server instead of simple database/file storage? Do we use some free mail server, or we must support some old infrastructure?
Re: forum.dlang.org, version 2 (BETA)
On Friday, 5 June 2015 at 02:14:08 UTC, Vladimir Panteleev wrote: On Friday, 5 June 2015 at 02:11:17 UTC, IgorStepanov wrote: On Friday, 5 June 2015 at 02:03:48 UTC, Vladimir Panteleev wrote: On Friday, 5 June 2015 at 02:02:11 UTC, IgorStepanov wrote: Sorry if this question has been raised but is the reason for the inability to edit posts? Posts are not editable because once sent, they are relayed to the NNTP server, mailing lists, and users' email inboxes. Will be this feature implemented sometime? Only when it becomes possible to edit a sent email. Ok, why we use email/NNTP server instead of simple database/file storage? Do we use some free mail server, or we must support some old infrastructure? Yes, this forum was created explicitly to replace Web-News, an NNTP newsreader. Initially all communication was done via NNTP (Web-News or a desktop NNTP client) and mailing lists. The last time I checked, only 50% of posters used forum.dlang.org, others used NNTP or mailing lists. Interestingly, they do it because they are accustomed, or they have a handy desktop/mobile applications?
Re: DIP66 1.2 (Multiple) alias this. Continuation of work.
Hello, comrades. Lets put into the schedule reviewing of this PR (https://github.com/D-Programming-Language/dmd/pull/3998). It contains a very much changes and it is hard to maintain its performance: each foreign PR may break it.
Re: Better handling of noncopyable objects and objects with this(this)
On Monday, 1 June 2015 at 04:43:20 UTC, Andrei Alexandrescu wrote: FYI I just created https://issues.dlang.org/show_bug.cgi?id=14638 as one of possibly several language enhancements to improve usability of noncopyable types (most allocators are not copyable) and to enhance performance of objects that define this(this). -- Andrei My last year's thoughts about that: https://issues.dlang.org/show_bug.cgi?id=13492
Re: Proof of concept - library AA
On Saturday, 30 May 2015 at 15:24:49 UTC, Adam D. Ruppe wrote: On Saturday, 30 May 2015 at 14:10:35 UTC, IgorStepanov wrote: static Foo opImplicitConstructFrom(T)(T val) if(is(T : int)) I briefly mentioned this at the dconf and thinking about it a bit more, I think there's only two cases where we want implicit construction: function argument lists and function return values. (The syntax doesn't really matter, but I'd do it similar to C++ and just slap an @implicit on a regular constructor). What did people say about this idea?
Re: Proof of concept - library AA
On Saturday, 30 May 2015 at 08:51:31 UTC, Vladimir Panteleev wrote: On Saturday, 30 May 2015 at 08:50:21 UTC, Vladimir Panteleev wrote: http://localhost/post/asvcbsvfcxznwypttojk@192.168.0.1 Sorry, working link: http://forum.dlang.org/post/asvcbsvfcxznwypttojk@192.168.0.1 We may say that AA!(K, V)* should be recognized as V[K] by compiler. However in this case we will found another problem: opIndexAssign will unable to allocate memory for the new AA instance, because it can't modify `this` pointer. Otherwise, implicit casting _from_ another type is a unsolvable task now. Please, remind, why we don't want to add possibility of implicit casting from another type (not as default constructor behaviour, of course)? Something like struct Foo { int a; static Foo opImplicitConstructFrom(T)(T val) if(is(T : int)) { return Foo(val); } }
Re: Proof of concept - library AA
struct Foo { int a; static Foo opImplicitConstructFrom(T)(T val) if(is(T : int)) { return Foo(val); } } void test(Foo foo, int i) { assert(foo.a == i); } test(42, 42); - test(Foo.opImplicitConstructFrom(42), 42);
Re: Proof of concept - library AA
On Friday, 29 May 2015 at 11:17:00 UTC, Martin Nowak wrote: On Wednesday, 27 May 2015 at 17:16:53 UTC, IgorStepanov wrote: Foo f; f[5][3] = Foo(42); translates to f.opIndex!(true)(5).opIndex!(true)(3) = Foo(42); auto x = f[5][4]; translates to auto x = f.opIndex!(false)(5).opIndex!(false)(3); We shouldn't replace opIndexAssign though, b/c default construction + assignment is more expensive than constructing in-place. Sorry, I meant f.opIndex!(true)(5).opIndexAssign(Foo(42), 3);
Re: Proof of concept - library AA
On Friday, 29 May 2015 at 12:52:29 UTC, Martin Nowak wrote: On Friday, 29 May 2015 at 11:22:53 UTC, IgorStepanov wrote: Sorry, I meant f.opIndex!(true)(5).opIndexAssign(Foo(42), 3); Added to the ER. https://issues.dlang.org/show_bug.cgi?id=7753#c6 Thanks, but unfortunately, writing enhacement request to bugzilla is equals to writing to /dev/null :) I'll create a DIP about this, when I'll have a free time. What do you want about this syntax? Maybe you may suggest a better solution?
Re: Proof of concept - library AA
On Friday, 29 May 2015 at 17:52:58 UTC, Martin Nowak wrote: On Friday, 29 May 2015 at 13:12:58 UTC, IgorStepanov wrote: What do you want about this syntax? Maybe you may suggest a better solution? The discussion drifts a little OT, if we have opIndexCreate, then the library AA can be more compatible, but it still won't be a drop-in replacement. We went a long way in this direction and if we don't come to the result yet, that, I think, we haven't thought-out plan. I suggest you to answer to the following two question: 1. What way to transit to the new AA would be acceptable? You rejects my way (and I could not continue to work in the winter), and AFAIR you principial objection was: aaLiteral is non-@safe for the unsafe code and it is breakage. However, aaLiteral attributes hasn't checked by compiler, because it was used in e2ir. _d_assocarrayliteralTX is not @safe or pure, but aa can be used in @safe pure code. We may insert additional checks, see aaLiteral attributes, and raise deprecation message, if attributes are inacceptable. If it is the last objection, we may repeat compiler-side part for the new AA template. Othrewice, lets invent another way. 2. What issues disallows us to implement full library AA? Except .stringof, .mangleof, and other compiler magic. I see only two issues: opIndexCreate and building aa from literals.
Re: DIP66 1.2 (Multiple) alias this. Continuation of work.
On Friday, 29 May 2015 at 21:41:12 UTC, Andrei Alexandrescu wrote: On 5/29/15 3:37 PM, IgorStepanov wrote: On Tuesday, 26 May 2015 at 00:03:43 UTC, Andrei Alexandrescu wrote: On 5/25/15 3:01 PM, IgorStepanov wrote: Ok, I've applied your changes to the DIP page, and I'm starting to rework my github PR. Sorry for the slow work (I'm very busy last time). However I still working. Stay on line=) Thanks. Please get this done and let's pull it in for 068. -- Andrei I've finished preparing of the github PR. Now it is ready for review. So https://github.com/D-Programming-Language/dmd/pull/3998 is the one? -- Andrei Yep.
Re: Proof of concept - library AA
On Friday, 29 May 2015 at 22:41:13 UTC, Martin Nowak wrote: On Friday, 29 May 2015 at 21:58:16 UTC, IgorStepanov wrote: I suggest you to answer to the following two question: 1. What way to transit to the new AA would be acceptable? One that doesn't break any code, carefully deprecates necessary semantic changes, and provides an improved implementation. You rejects my way (and I could not continue to work in the winter), and AFAIR you principial objection was: aaLiteral is non-@safe for the unsafe code and it is breakage. The objection was too much code breakage for an inferior implementation. https://github.com/D-Programming-Language/druntime/pull/934#issuecomment-66888409 We may insert additional checks, see aaLiteral attributes, and raise deprecation message, if attributes are inacceptable. I recall a list of your demands. 1. open addressing 2. efficient construction, insertion and assignment (no extra copies or postblits) 3. fully CTFEable (includes storing literals in the data segment) 4. type and attribute correctness 5. get's rid of TypeInfo methods (toHash, opEquals, tsize) 6. GC NO_SCAN for values 1. It depends only on library AA implementation, not on dmd-druntime interaction. 2. This goal was achieved in my last AA version. Open addressing edition may get us troubles with it, but I think this troubles are solvable. 3. Storing CTFE literals was implemented in that implementation. Maybe not quite right, but the problem is also solvable. 4. This solution follows directly from template implementation. 5. Was done. 6. This problem is also solvable. Now about backward compatibility: AFAIR you pointed to the one breakage: the forced checking of attribute correctness. I then entered into an argument with you, but I forgot, that the forced attribute checking was disabled in the last edition: Yes, aaLiteral gets attributes from the underlying code, and if AA constructor was unsafe, aaLiteral was unsafe too. However, AssocArrayLiteralExp::toElem doesn't check the attribute correctness and constructing of unsafe or non-throwable AA from safe or throwable code was allowed. Or I forgot about some other breakage cases? 2. What issues disallows us to implement full library AA? Except .stringof, .mangleof, and other compiler magic. I see only two issues: opIndexCreate and building aa from literals. - error messages - attributes - literals (especially polysemous initializers, i.e. ubyte[ubyte] aa = [0:1, 1:2]) - implicit tail const conversion Val[Key] - const(Val)[Key] - lots of magic around making Key const - delete aa[key] - lots of other small details (grep for Taarray in src/expression.c) This is a heavily used built-in type, we can't risk a rewrite that breaks lots of code. - error messages Is it a significant problem? If compiler allows correct code, disallows incorrect code and gets a clear error messages there there is no problem, I think. Of cource, we will need to write pretty error messages, implement correct .stringof in dmd (to writting type name as V[K], not as AA!(K, V). - attributes We will able to deprecate attribute violations in transitional version (with vtbl). - literals (especially polysemous initializers, i.e. ubyte[ubyte] aa = [0:1, 1:2]) Yes, this is the first main trouble. - implicit tail const conversion Val[Key] - const(Val)[Key] May be we may add alias this-es for all all those variants? Something like ... struct AA(K, V) { alias getCKeyMval this; alias getMKeyCval this; alias getCKeyCval this; @property { ref AA!(const(K), V) getCKeyMval() { return *cast(typeof(return)*)this; } ref AA!(K, const(V)) getMKeyCval() { return *cast(typeof(return)*)this; } ref AA!(const(K), const(V)) getCKeyCval() { return *cast(typeof(return)*)this; } } } - lots of magic around making Key const The most count of them may be solved without language modifying. - delete aa[key] This case has gone one or two years ago. Now for the following code... int[int] aa; delete aa[5]; ... compiler writes me Error: cannot delete type int - lots of other small details (grep for Taarray in src/expression.c) We should start to try to find and solve them. As I see, we now we need only two language change requirements: opIndexCreate and AA literal overloading. The rest of the problems can be identified at the stage of parallel operation of both implementations.
Re: DIP66 1.2 (Multiple) alias this. Continuation of work.
On Tuesday, 26 May 2015 at 00:03:43 UTC, Andrei Alexandrescu wrote: On 5/25/15 3:01 PM, IgorStepanov wrote: Ok, I've applied your changes to the DIP page, and I'm starting to rework my github PR. Sorry for the slow work (I'm very busy last time). However I still working. Stay on line=) Thanks. Please get this done and let's pull it in for 068. -- Andrei I've finished preparing of the github PR. Now it is ready for review.
Re: Proof of concept - library AA
On Wednesday, 27 May 2015 at 14:12:02 UTC, Martin Nowak wrote: On Sunday, 24 May 2015 at 15:13:41 UTC, Vladimir Panteleev wrote: Could you elaborate on what these magic semantics are? and no easy solution exists for the ++aa[key1][key2] case. Is this specific to the pre-increment? aa[key1][key2]++ is generally a useful pattern. This applies to pre/post increment as well as assignment and opOpAssign. When an lvalue is needed the compiler will call a special runtime function GetX to obtain an lvalue for aa[key1], i.e. the entry will be default initialized iff missing. If the expression is an rvalue though (aa[key1][key2]), a missing key1 will trigger a range error. In an opIndex(Key) you have no idea whether the whole expression I an lvalue or an rvalue. IIRC the construction/assignment of a value is also handled specifically. BTW, may be we should create DIP about opIndex extending? What do you want about following? Let, if opIndex is template, and the first template argument is a bool value, compiler should pass true, if this is a part of l-value expression: struct Foo { Foo[] children; this(int value) { this.value = value; } int value; ref Foo opIndex(bool lvl)(size_t idx) { if (idx children.length) return children[idx]; static if (lvl) { children.length = idx + 1; return children[idx]; } else { throw new Exception(out of bounds); } } } Foo f; f[5][3] = Foo(42); translates to f.opIndex!(true)(5).opIndex!(true)(3) = Foo(42); auto x = f[5][4]; translates to auto x = f.opIndex!(false)(5).opIndex!(false)(3);
Re: DIP66 1.2 (Multiple) alias this. Continuation of work.
On Tuesday, 31 March 2015 at 20:01:14 UTC, Andrei Alexandrescu wrote: On 3/31/15 7:28 AM, IgorStepanov wrote: On Monday, 30 March 2015 at 18:33:17 UTC, Andrei Alexandrescu wrote: On 3/30/15 8:04 AM, Steven Schveighoffer wrote: On 3/29/15 1:34 PM, IgorStepanov wrote: 1. We should reject types which use opDispatch and alias this at the same time. Why? Alias this has no filter. opDispatch can use template constraints. It makes perfect sense to prefer opDispatch, unless it doesn't have a valid match, and then use alias this instead. For example, if I wanted to wrap a type so I can instrument calls to 'foo', I could do something like this: struct FooWrapper(T) { T t; alias t this; auto opDispatch(string s, A...)(A args) if(s == foo) { writeln(calling foo); return t.foo(args); } } Why is this a bad use case? The idea is to start restrictive and define interaction meaningfully later based on compelling use cases. -- Andrei Andrei, do you approve those changes? Can we move to work on my github PR? I made a few editorial passes, no major changes. I think there's still a fly in the ointment. The resolution algorithm goes: 1. If xyz is a symbol (member, method, enum etc) defined inside typeof(obj) then lookup is done. 2. Otherwise, if xyz is a symbol introduced in the base class (where applicable), then lookup is done. 3. Otherwise, if opDispatch!xyz exists, then lookup is done. 4. Otherwise, alias this is attempted transitively, and if xyz is found, then lookup is done. 5. Otherwise an UFCS rewrite is effected. This puts opDispatch in between inheritance and subtyping, which I think we discussed is inappropriate - alias this should be effectively subtyping. If we're really convinced alias this means multiple subtyping, the inherited type should not have a special role. However, it simplifies a lot of things to give one particular subtype a leg up on all others. So I think this would work: 1. If xyz is a symbol (member, method, enum etc) defined inside typeof(obj) then lookup is done. 2. Otherwise, if xyz is a symbol introduced in the base class (where applicable), then lookup is done. 3. Otherwise, if xyz is found at least via either an opDispatch!xyz or alias this conversion, then lookup is done. 4. Otherwise an UFCS rewrite is effected. Then you explain that if you find more than one possibility via opDispatch and alias this, that's an ambiguity error. I noticed you do mention in the Limitations section that opDispatch and alias this cannot be simultaneously present, but that kind of contradicts your resolution algorithm. Andrei Ok, I've applied your changes to the DIP page, and I'm starting to rework my github PR. Sorry for the slow work (I'm very busy last time). However I still working. Stay on line=)
Re: DIP66 1.2 (Multiple) alias this. Continuation of work.
On Monday, 25 May 2015 at 23:36:00 UTC, Timon Gehr wrote: On 05/26/2015 01:17 AM, IgorStepanov wrote: My example: auto foo(T)(){ return 1; } auto foo(T)()if(is(T: int)){ return 2; } struct Foo { //... } struct Bar { Foo f; int i; alias i this; } auto ret = foo!Bar; assert(ret == 2); //exactly the second function ... (No, this actually raises an ambiguity error, but I see the point.) After that, imagine, someone added alias intVal this to Foo. Now there are many ways to convert Bar to int. However, user don't know about it. And if foo!Bar will start to return 1, it is very ugly situation. I'm not convinced the alternative is better. One can still do e.g.: auto foo(T)()if(!is(typeof({static assert(is(T: int));}))){ return 1; } auto foo(T)()if(is(typeof({static assert(is(T: int));}))){ return 2; } Yes, we will able hack the situation: for example via __traits(compile, ...) or via is(typeof(...)) However all those hacks are known and in strange template behaviour we able to find all those places. I think, error is not bad. Multiple alias this is a new feature, and this solution allows us to catch all error and safely try this feature. Later we will able to relax all rules, when we will sure that it is safe.
Re: Proof of concept - library AA
On Sunday, 24 May 2015 at 14:13:26 UTC, Martin Nowak wrote: Would be interesting to get some opinions on this. https://github.com/D-Programming-Language/druntime/pull/1282 BTW, I have one idea. We may declare the AA ABI: AA is a pointer to the next layout: __vtbl N bytes of data and __vtbl is a pointer to a struct: struct AAVTBL { size_t function() size; //returns the size of AA object: N + (AABTBL*).sizeof void* get(void* aa, @somefields@) //another function } And it we teach compiler to use this ABI, we may say that any type which follows this ABI is a correct associative array. At the first stage, we may adapt the existing AA implementation to follow this ABI, and adapt the compiler to use it. After that, we will adble to write library AA (which also follows this ABI), which may be constructed as general D type, and casted to AA later.
Re: Proof of concept - library AA
On Sunday, 24 May 2015 at 15:13:41 UTC, Vladimir Panteleev wrote: On Sunday, 24 May 2015 at 14:13:26 UTC, Martin Nowak wrote: Would be interesting to get some opinions on this. https://github.com/D-Programming-Language/druntime/pull/1282 Looks like a good step in the right direction. Some questions about: This provides a strong incentive to no longer use the magic semantics of the builtin AAs, Could you elaborate on what these magic semantics are? and no easy solution exists for the ++aa[key1][key2] case. Is this specific to the pre-increment? aa[key1][key2]++ is generally a useful pattern. The general idea that library aa has a vtbl, and standart AA fuctions like _aaGetX will access to our AA via vtbl. Compiler will operate with aa via all those _aaGetX, _aaLen et c. Thus aa[x][y]++; will work as early.
Re: DIP66 1.2 (Multiple) alias this. Continuation of work.
On Monday, 25 May 2015 at 22:32:55 UTC, Timon Gehr wrote: On 03/29/2015 07:34 PM, IgorStepanov wrote: 3. is(T: B) should raise an error if there are many ways to convert T to B. This is inconsistent with how 'is' works otherwise, and it breaks template constraints in annoying ways. (There is no SFINAE.) auto foo()()if(true){ return 1; } // this is the one you want auto foo()()if(a){ return 2; }// this is the one with is(T: B) void main(){ foo(); // error } Is the intention that no types with multiple alias this paths to some type should be defined in the first place? This problem was discussed early, and Andrey sad that is(D: B) should raise a error, if D can be converted to B via multiple ways. I mostly agree with Andrey. The my main argument: wrong convertion shouldn't be implicitly hidden and I suggested return true even if there are many ways to convertion. My example: auto foo(T)(){ return 1; } auto foo(T)()if(is(T: int)){ return 2; } struct Foo { //... } struct Bar { Foo f; int i; alias i this; } auto ret = foo!Bar; assert(ret == 2); //exactly the second function After that, imagine, someone added alias intVal this to Foo. Now there are many ways to convert Bar to int. However, user don't know about it. And if foo!Bar will start to return 1, it is very ugly situation.
Re: DIP66 1.2 (Multiple) alias this. Continuation of work.
On Monday, 30 March 2015 at 18:33:17 UTC, Andrei Alexandrescu wrote: On 3/30/15 8:04 AM, Steven Schveighoffer wrote: On 3/29/15 1:34 PM, IgorStepanov wrote: 1. We should reject types which use opDispatch and alias this at the same time. Why? Alias this has no filter. opDispatch can use template constraints. It makes perfect sense to prefer opDispatch, unless it doesn't have a valid match, and then use alias this instead. For example, if I wanted to wrap a type so I can instrument calls to 'foo', I could do something like this: struct FooWrapper(T) { T t; alias t this; auto opDispatch(string s, A...)(A args) if(s == foo) { writeln(calling foo); return t.foo(args); } } Why is this a bad use case? The idea is to start restrictive and define interaction meaningfully later based on compelling use cases. -- Andrei Andrei, do you approve those changes? Can we move to work on my github PR?
Re: DIP66 1.2 (Multiple) alias this. Continuation of work.
On Monday, 30 March 2015 at 15:04:20 UTC, Steven Schveighoffer wrote: On 3/29/15 1:34 PM, IgorStepanov wrote: 1. We should reject types which use opDispatch and alias this at the same time. Why? Alias this has no filter. opDispatch can use template constraints. It makes perfect sense to prefer opDispatch, unless it doesn't have a valid match, and then use alias this instead. For example, if I wanted to wrap a type so I can instrument calls to 'foo', I could do something like this: struct FooWrapper(T) { T t; alias t this; auto opDispatch(string s, A...)(A args) if(s == foo) { writeln(calling foo); return t.foo(args); } } Why is this a bad use case? -Steve You can split this code to two structs: struct FooWrapper(T) { struct FooDispatcher { auto opDispatch(string s, A...)(A args) { writeln(calling , s); } } FooDispatcher d; T t; alias t this; auto foo(string s, A...)(A args) { writeln(calling foo); return t.foo(args); } } FooWrapper!X x; x.foo(1, 2); //FooWrapper.foo has been called x.bar(1, 2); //FooWrapper.d.opDispatch has been called X orig = x; //FooWrepper.t is returned. === Yes, this code is much more tricky, but it work as you wish. opDispatch + alias this may deliver many problems, if one of those has more high priority then the other. We want to implement alias this maximally strictly, and after that, try to find rules which may be safely relaxed.
Re: DIP66 1.2 (Multiple) alias this. Continuation of work.
On Sunday, 29 March 2015 at 08:03:37 UTC, deadalnix wrote: On Saturday, 28 March 2015 at 19:52:15 UTC, IgorStepanov wrote: http://wiki.dlang.org/DIP66 First I want to apologize for the long absence. I was very busy for those months and now I ready to continue the work. This DIP has been approved with three clarifications: about is-expression, about opDispatch and about common inheritance. I've reflected those clarifications in this DIP version, and if it is OK, I'll start adaptation of my github PR to this DIP. Can you explain the change from current situation a bit ? Do you mean changes from previous DIP version? I've added three ideas to this version: 1. We should reject types which use opDispatch and alias this at the same time. 2. We want to semantic alias this at the same time as an inheritance (and disallow hidding of alias this-ed members by inherited members), but we shouldn't do this change now, because it may break a lot of existing code. 3. is(T: B) should raise an error if there are many ways to convert T to B. If you ask about changes from current alias this implementation, this DIP introduces multiple alias this, and formally discribes different cases.
DIP66 1.2 (Multiple) alias this. Continuation of work.
http://wiki.dlang.org/DIP66 First I want to apologize for the long absence. I was very busy for those months and now I ready to continue the work. This DIP has been approved with three clarifications: about is-expression, about opDispatch and about common inheritance. I've reflected those clarifications in this DIP version, and if it is OK, I'll start adaptation of my github PR to this DIP.
Re: DIP66 v1.1 (Multiple) alias this.
On Saturday, 20 December 2014 at 21:25:28 UTC, Andrei Alexandrescu wrote: On 11/2/14 6:57 AM, IgorStepanov wrote: And there is dispute about is expression: see http://forum.dlang.org/thread/ubafmwvxwtolhmnxb...@forum.dlang.org?page=5 OK, time to get this approved. First, the current DIP doesn't seem to address this: Walter and I would agree to making the presence of BOTH alias this and opDispatch a compile-time error. That would break existing code but not change semantics silently. Far as I remember it was left to the discussion. Nobody objected to this issue, thus we may accept it. I think. Any thoughts on this? Currently opDispatch gets priority over alias this, see lookup step 3 in section Semantics of http://wiki.dlang.org/DIP66. That's problematic because it puts opDispatch in _between_ normal subtyping via inheritance and alias this, which is supposed to be just as solid as inheritance. I think the principled solution is to combine steps 2 and 4 into step 2, i.e. alias this is as strong as inheritance. Any ambiguous symbols would be rejected. The second possibility, less principled but probably practical, would be to swap steps 3 and 4. That way alias this has juuust a teensy bit a lower status than regular inheritance. It looks nice, but it can greatly break the existing code. I suggest a postpone this issue and discuss the semantic order in a separate discusson/ The simplest thing (which Walter favors) is to make the presence of both opDispatch and alias this a compile-time error. That would break only a teensy amount of code if any, and would give us time to investigate the best approach when compelling use cases come about. So I suggest we move forward with that for this DIP. Regarding the is-expression controversy in http://forum.dlang.org/thread/ubafmwvxwtolhmnxb...@forum.dlang.org?page=5: First off, is(S : T) is a subtyping test - is S a non-proper subtype of T, or not? (Non-proper or improper subtyping: S is allowed to be identical to T). alias this is a mechanism that introduces subtyping. It follows that subtyping introduced via alias this must be detected with is-expressions. Now, you give an example of subtyping where one or more two objects of the same supertype may be reached through two or more different paths. This is a well-known problem in subtyping (known as diamond hierarchy or repeated inheritance). In the case of alias this, different objects of the same type may be reachable (or at least the compiler is unable to tell statically whether the objects are distinct or not). A correct but hamfisted solution would be to sever the subtyping relationship whenever the same type is reachable through multiple paths. The versatility of alias this, however, suggests a better solution: if T is indirectly reachable as a supertype of S through more than one path and the subtyping is either tested (by means of an is-expression) or effected (by means of an implicit conversion), the compiler should issue a compile-time error asking the user to define an alias this DIRECTLY inside S, which takes precedence over indirect reachability and informs the type system which T of the several reachable ones is needed. Please let me know of any thoughts. Thanks! Summing up. There are three way to process is(D: B) where D may be converted to B in several ways. 1. is(D: B) should return false: D is not subtype of B now. 2. is(D: B) should return true: D is subtype of B anyway. 3. is(D: B) should raise an error: let the user decide what he wants. I strongly aganist the first way. It means that is(D: B) may absorb the real error, if it happens. Now only two construction in D may absorb errors: is(typeof(something)) and __traits(compiles, anything)). I say absorb when compiler see the error, ignores it and changes way of compilation: static if (noErrors) correct branch else error branch This situation may cause strage errors, code hijacking and other bad things, thus user should has a possibility to keep track of such cases. is(typeof(something)) and __traits(compiles, anything)) is a special constructions to error handling and user and everyone understands what is expected. is(D: B) is trusted construction and it can't create problems now. Let's leave it so. The second way is better, I think. It doesn't absorb the error, it skip error but doesn't change the compilation way. Error will be raised anyway when compiler will process code which use this casting. void foo(D)(D obj) if (is(D: Base)) // compiler will skip the error here... { Base b = obj; //... but it will raise the error here. } The third way is correct too, I think. It raises error earlier, but I changes current `is` semantic. AFAIK, `is` doesn't raise errors now. the compiler should issue a compile-time error asking the user to define an alias this DIRECTLY inside S, which takes precedence over indirect reachability and informs the
Re: DIP66 v1.1 (Multiple) alias this.
Bump
Re: Operator Overloading in an Abstract Base Class
On Thursday, 20 November 2014 at 22:10:28 UTC, Nordlöw wrote: In my module symbolic.d at https://github.com/nordlow/justd/blob/master/symbolic.d is it somehow possible to overload binary operator ~ on pairs of instances SPatt? I'm asking because my current try at https://github.com/nordlow/justd/blob/master/symbolic.d#L332 is not called in the unittest at https://github.com/nordlow/justd/blob/master/symbolic.d#L347 Isn't it possible to do operator overloading in an abstract base-class? Seq opBinary(string op)(Patt rhs) { static if (op == ~) { return seq(this, rhs); } else { static assert(false, Unsupported binary operator ~ op); } } As far I understood your question, you want to override opBinary in a derived class? You can't do it directly, because opBinary is template and template can not be overriten. However, you may declare virtual non-template method for ~ and call it from opBinary. And `static if (op == ~)` is not good. Use template constraint instead: class Patt { // It is better then your static if. // Compiler will raise better error message for incorrect operator. Seq opBinary(string op)(Patt rhs) if (op == ~) { return opCatImpl(rhs); } protected Seq opCatImpl(Patt rhs) //you may override it in derived class { return seq(this, rhs); } }
Re: Dynamic array as stack and GC.BlkAttr.APPENDABLE
On Sunday, 16 November 2014 at 09:28:25 UTC, Rainer Schuetze wrote: On 15.11.2014 23:40, IgorStepanov wrote: Do I any fundamental error in this code? May be Bucket[] ret = new Bucket[len]; ret.ptr is not base pointer? Yes, for arrays larger than 2kB, the allocation length information is placed before the actual data, and ret.ptr has an offset of 16 bytes into the allocation block. As a consequence, applying BlkAttr.NO_INTERIOR will very likely cause the block to be collected. NO_INTERIOR has no effect for smaller blocks. That's why the existing AA code explicitely uses GC.malloc instead of new[]. Thank you very much. That explains a lot.
Re: Dynamic array as stack and GC.BlkAttr.APPENDABLE
On Saturday, 15 November 2014 at 03:41:56 UTC, Steven Schveighoffer wrote: On 11/14/14 8:56 PM, IgorStepanov wrote: On Friday, 14 November 2014 at 23:49:00 UTC, ketmar via Digitalmars-d wrote: On Fri, 14 Nov 2014 23:23:17 + IgorStepanov via Digitalmars-d digitalmars-d@puremagic.com wrote: What does the NO_INTERIOR flag? it stops GC to acknowledge pointers inside allocated area as anchors. i.e. if there is no pointer to the head (first address) of allocated memory, it is assumed to be garbage. this way we have much less false pointers, and GC not doing pointer-block conversions. for buckets we certainly has head pointer and can't have pointers to bucket elements without head pointer. so it's safe to tell GC that it shouldn't do unnecessary work. In other words, if buckets array will contain only uint-s there is no reason to mark buckets with NO_INTERIOR? In case ketmar's reply doesn't drive it home... NO_INTERIOR means that the GC should NOT consider pointers to a block as valid references if those pointers don't point to the HEAD of the block. In other words, they point to the interior. An example int * x = new int; *x = 5; byte *b = cast(byte *)x; b++; // b is now an interior pointer, yet x is not. If we had marked x's block as NO_INTERIOR, we are fine as long as x remains. If we set x to null, there is a danger that the block is collected, and now b is dangling. Now, this isn't a whole lot more efficient, you still have to look up what b points at and see that it has the flag set. BUT, where it DOES help is if you had some size_t somewhere on a stack, that happens to be the same value as b, the GC may think it's a pointer, but correctly not keep the block alive just for that false pointer. The larger the block, the more helpful NO_INTERIOR is. -Steve Thanks for explanations. I undersood that now. However I have got a strange bug with NO_INTERIOR. I have the following function: static Bucket[] newBuckets(in size_t len) @trusted pure nothrow { Bucket[] ret = new Bucket[len]; if (!__ctfe) { GC.setAttr(ret.ptr, GC.BlkAttr.NO_INTERIOR); } return ret; } The Bucket declaration has a two uint fields and one of those has a explicit initializer: private enum uint EmptyBucket = uint.max; static struct Bucket { uint depth; uint index = EmptyBucket; @property bool occupied() const { return index != EmptyBucket; } } I call this function for creating new bucket array (in init() function, at first value inserting and at rehashing). I don't create a copy, slice, I don't get a array pointer. Before array creating and rehashing I use buckets only for access by index. However I have got a error, when `index` field initializes with some strange value when newBuckets argument is very big. (buckets.length is big value). When I comment a GC.setAttr(ret.ptr, GC.BlkAttr.NO_INTERIOR); line, code start works fine. Do I any fundamental error in this code? May be Bucket[] ret = new Bucket[len]; ret.ptr is not base pointer?
Dynamic array as stack and GC.BlkAttr.APPENDABLE
Recently I encountered the following problem. I need a simple stack of uint. I want to push uints back and pop it. I don't want to copy this stack and I want it to work fast. In a first approximation, the problem seems easy. uint[] my_stack; my_stack.reserve(256); my_stack ~= 1; //push uint head = my_stack[$ - 1]; //top my_stack.length--; //pop my_stack ~= 2; //push However, when I changes the length and append new elem to the array after it, array is reallocated. Ok, good point. Seems reasonable. I might create slice of array copy before changing of the length. And by default changing array length or creating slice should cause an array reallocation at appending. However I don't plan to copy or creating slices of this array, thus I need to some magic for avoid reallocation. I've found a GC.BlkAttr.APPENDABLE flag at core.memory. How ever it hasn't helped me. Is there another way to do it without manually managing of memory?
Re: Dynamic array as stack and GC.BlkAttr.APPENDABLE
On Friday, 14 November 2014 at 22:25:20 UTC, Dmitry Olshansky wrote: 15-Nov-2014 01:16, IgorStepanov пишет: Recently I encountered the following problem. I need a simple stack of uint. I want to push uints back and pop it. I don't want to copy this stack and I want it to work fast. In a first approximation, the problem seems easy. uint[] my_stack; my_stack.reserve(256); my_stack ~= 1; //push uint head = my_stack[$ - 1]; //top my_stack.length--; //pop Just make push into: my_stack.assumeSafeAppend(); my_stack ~= value; To avoid relocations. Thanks! Your magic is works!
Re: Dynamic array as stack and GC.BlkAttr.APPENDABLE
On Friday, 14 November 2014 at 22:38:53 UTC, Steven Schveighoffer wrote: On 11/14/14 5:25 PM, Dmitry Olshansky wrote: 15-Nov-2014 01:16, IgorStepanov пишет: Recently I encountered the following problem. I need a simple stack of uint. I want to push uints back and pop it. I don't want to copy this stack and I want it to work fast. In a first approximation, the problem seems easy. uint[] my_stack; my_stack.reserve(256); my_stack ~= 1; //push uint head = my_stack[$ - 1]; //top my_stack.length--; //pop Just make push into: my_stack.assumeSafeAppend(); my_stack ~= value; To avoid relocations. In actuality, you do not need this before every push. You only need it between a pop and a push. I highly recommend not doing arrays-as-stacks, because you end up writing your own type. I'm working on AssociativeArray implementation and I need to array which contains indices of free entries (entries is an array which contain Entry objects. When we need to find empty Entry to add a new value to the AA, we are getting the first element of the stack. (If the stack is empty, we need to add new element to entries). If entry has been removed from AA, it index is added to the stack). Thus I don't want to add extra fields, and this stack is an encapsulated entity.
Re: Dynamic array as stack and GC.BlkAttr.APPENDABLE
On Friday, 14 November 2014 at 23:12:05 UTC, IgorStepanov wrote: On Friday, 14 November 2014 at 22:38:53 UTC, Steven Schveighoffer wrote: On 11/14/14 5:25 PM, Dmitry Olshansky wrote: 15-Nov-2014 01:16, IgorStepanov пишет: Recently I encountered the following problem. I need a simple stack of uint. I want to push uints back and pop it. I don't want to copy this stack and I want it to work fast. In a first approximation, the problem seems easy. uint[] my_stack; my_stack.reserve(256); my_stack ~= 1; //push uint head = my_stack[$ - 1]; //top my_stack.length--; //pop Just make push into: my_stack.assumeSafeAppend(); my_stack ~= value; To avoid relocations. In actuality, you do not need this before every push. You only need it between a pop and a push. I highly recommend not doing arrays-as-stacks, because you end up writing your own type. I'm working on AssociativeArray implementation and I need to array which contains indices of free entries (entries is an array which contain Entry objects. When we need to find empty Entry to add a new value to the AA, we are getting the first element of the stack. (If the stack is empty, we need to add new element to entries). If entry has been removed from AA, it index is added to the stack). Thus I don't want to add extra fields, and this stack is an encapsulated entity. And if we talk about AA: What does the NO_INTERIOR flag? Why the table (buckets array) is allocated with GC.calloc and NO_INTERIOR flag instead of new Entry[size];
Re: Dynamic array as stack and GC.BlkAttr.APPENDABLE
On Friday, 14 November 2014 at 23:49:00 UTC, ketmar via Digitalmars-d wrote: On Fri, 14 Nov 2014 23:23:17 + IgorStepanov via Digitalmars-d digitalmars-d@puremagic.com wrote: What does the NO_INTERIOR flag? it stops GC to acknowledge pointers inside allocated area as anchors. i.e. if there is no pointer to the head (first address) of allocated memory, it is assumed to be garbage. this way we have much less false pointers, and GC not doing pointer-block conversions. for buckets we certainly has head pointer and can't have pointers to bucket elements without head pointer. so it's safe to tell GC that it shouldn't do unnecessary work. In other words, if buckets array will contain only uint-s there is no reason to mark buckets with NO_INTERIOR?
Re: C++ overloaded operators and D
On Thursday, 13 November 2014 at 09:57:04 UTC, Marc Schütz wrote: On Wednesday, 12 November 2014 at 21:17:42 UTC, IgorStepanov wrote: On Wednesday, 12 November 2014 at 20:49:42 UTC, Marc Schütz wrote: On Wednesday, 12 November 2014 at 19:32:32 UTC, IgorStepanov wrote: On Wednesday, 12 November 2014 at 14:41:17 UTC, Marc Schütz wrote: On Wednesday, 12 November 2014 at 11:43:36 UTC, IgorStepanov wrote: C++ and D provides different behaviour for operator overloading. D has a opIndex + opIndexAssign overloads, and if we want to map opIndex to operator[], we must to do something with opIndexAssign. operator[] can be mapped to opIndex just fine, right? Only opIndexAssign wouldn't be accessible from C++ via an operator, but that's because the feature doesn't exist. We can still call it via its name opIndexAssign. operator and operator can't be mapped to D. Same for operator. That's true. Maybe we can just live with pragma(mangle) for them, but use D's op... for all others? Binary arithmetic operators can't be mapped to D, if them implemented as static functions: Foo operator+(int a, Foo f); //unable to map it to D, because static module-level Foo opAdd(int, Foo) will not provide the same behaviour as operator+ in D. Thus: C++ and D overloaded operators should live in different worlds. Can't we map both static and member operators to opBinary resp. opBinaryRight members in this case? How likely is it that both are defined on the C++ side, and if they are, how likely is it that they will behave differently? opBinary(Right) is a template-functions. You can't add previous declaration for it to struct: //C++ struct Foo { Foo operator+(const Foo); }; Foo operator+(int, const Foo); //D extern(C++) struct struct Foo { Foo opBinary!+(const ref Foo); //??? I see... } Foo opBinary!+(int, const ref Foo); //??? But this would of course be opBinaryRight, and inside struct Foo. What if Foo operator+(const Bar, const Foo);? Is it Foo.opBinaryRight, or Bar.opBinary, or both? For a C++ class interfaced from D: opBinary() in whichever of the two classes it is defined. For a D class interfaced from C++: choose one, preferably opBinary(), as it's the natural one. It is too difficult, I think. 1. Compiler should generate static operator declaration (for linkage with C++) and method-wrapper. 2. We should use old non-template operators (like opAdd, opSub etc.) or introduce new kind of operators. 3. We should explain to the user how to use our operator bindings (explain to the user). Anyway we may implement generic approach with pragma(mangle) now and add special rules for some operators if it be considered usefull. AFAIK, There are many objections aganist operators mapping are mentioned in n.g.
Re: C++ overloaded operators and D
On Wednesday, 12 November 2014 at 02:37:52 UTC, deadalnix wrote: On Tuesday, 11 November 2014 at 22:26:48 UTC, IgorStepanov wrote: Now D provides very powerfull means to link C++ code with D. However D doesn't allow to call C++ overloaded operators. It's very annoying, because C++ code may don't provide non-operator analogues. What we know about C++ overloadable operators? Overloaded operator in C++ is a trivial function/method with special name. Thus operator[](int) differs from op_index(int) function only by mangle. C++ OO have a different behaviour from D OO (for example C++ allows different and operator overloads or static function fro binary operators), thus we should avoud the temptation of map C++ OOs to D OOs, or back. Also D provides a pragma(mangle) which allows to redefine symbol mangle. It takes a string argument and redefine mangle to it: pragma(mangle, foo) void bar();//bar.mangleof == foo I suggest to modify pragma(mangle) to support C++ operators. If argument of this pragma is identifier (for example cppOpAdd), the pragma applied to extern(C++) function or method, compiler mangle the function in accordance with this identifier. //C++ struct Foo { int operator[](int); //another fields }; //D extern(C++) struct Foo { pragma(mangle, cppOpIndex) ref int op_index(int); //another fields } //using: Foo f; f.op_index(1)++; //OK, op_index is linked with Foo::operator[] f[1]++; //Error, no special behaviour for op_index I think this approach is simple, doesn't modify the language, can be easily implemented and usefull. Destroy! Why would you want to go that road ? Souldn't extern(C++) struct mangle this the right way by themselves ? C++ and D provides different behaviour for operator overloading. D has a opIndex + opIndexAssign overloads, and if we want to map opIndex to operator[], we must to do something with opIndexAssign. operator and operator can't be mapped to D. Same for operator. Binary arithmetic operators can't be mapped to D, if them implemented as static functions: Foo operator+(int a, Foo f); //unable to map it to D, because static module-level Foo opAdd(int, Foo) will not provide the same behaviour as operator+ in D. Thus: C++ and D overloaded operators should live in different worlds.
Re: C++ overloaded operators and D
On Wednesday, 12 November 2014 at 14:41:17 UTC, Marc Schütz wrote: On Wednesday, 12 November 2014 at 11:43:36 UTC, IgorStepanov wrote: C++ and D provides different behaviour for operator overloading. D has a opIndex + opIndexAssign overloads, and if we want to map opIndex to operator[], we must to do something with opIndexAssign. operator[] can be mapped to opIndex just fine, right? Only opIndexAssign wouldn't be accessible from C++ via an operator, but that's because the feature doesn't exist. We can still call it via its name opIndexAssign. operator and operator can't be mapped to D. Same for operator. That's true. Maybe we can just live with pragma(mangle) for them, but use D's op... for all others? Binary arithmetic operators can't be mapped to D, if them implemented as static functions: Foo operator+(int a, Foo f); //unable to map it to D, because static module-level Foo opAdd(int, Foo) will not provide the same behaviour as operator+ in D. Thus: C++ and D overloaded operators should live in different worlds. Can't we map both static and member operators to opBinary resp. opBinaryRight members in this case? How likely is it that both are defined on the C++ side, and if they are, how likely is it that they will behave differently? opBinary(Right) is a template-functions. You can't add previous declaration for it to struct: //C++ struct Foo { Foo operator+(const Foo); }; Foo operator+(int, const Foo); //D extern(C++) struct struct Foo { Foo opBinary!+(const ref Foo); //??? } Foo opBinary!+(int, const ref Foo); //??? May be some cases can be mapped to D, but these cases require special consideration. I suggest a generic rule. extern(C++) struct struct Foo { pragma(mangle, cppOpAdd)Foo op_add(const ref Foo); } extern(C++) pragma(mangle, cppOpAdd)Foo op_add2(int, const ref Foo); Now, if you want to use this overloaded operators as D operators, you may wrap it to D operator-functions. extern(C++) struct struct Foo { pragma(mangle, cppOpAdd) Foo op_add(const ref Foo); Foo opBinary(string s)(const ref Foo rvl) if (s == +) { return op_add(rvl); } Foo opBinaryRight(string s)(int lvl) if (s == +) { return op_add2(lvl, this); } } extern(C++) pragma(mangle, cppOpAdd)Foo op_add2(int, const ref Foo); This way allows access to C++ operators and doesn't add new rules into the language.
Re: C++ overloaded operators and D
On Wednesday, 12 November 2014 at 20:49:42 UTC, Marc Schütz wrote: On Wednesday, 12 November 2014 at 19:32:32 UTC, IgorStepanov wrote: On Wednesday, 12 November 2014 at 14:41:17 UTC, Marc Schütz wrote: On Wednesday, 12 November 2014 at 11:43:36 UTC, IgorStepanov wrote: C++ and D provides different behaviour for operator overloading. D has a opIndex + opIndexAssign overloads, and if we want to map opIndex to operator[], we must to do something with opIndexAssign. operator[] can be mapped to opIndex just fine, right? Only opIndexAssign wouldn't be accessible from C++ via an operator, but that's because the feature doesn't exist. We can still call it via its name opIndexAssign. operator and operator can't be mapped to D. Same for operator. That's true. Maybe we can just live with pragma(mangle) for them, but use D's op... for all others? Binary arithmetic operators can't be mapped to D, if them implemented as static functions: Foo operator+(int a, Foo f); //unable to map it to D, because static module-level Foo opAdd(int, Foo) will not provide the same behaviour as operator+ in D. Thus: C++ and D overloaded operators should live in different worlds. Can't we map both static and member operators to opBinary resp. opBinaryRight members in this case? How likely is it that both are defined on the C++ side, and if they are, how likely is it that they will behave differently? opBinary(Right) is a template-functions. You can't add previous declaration for it to struct: //C++ struct Foo { Foo operator+(const Foo); }; Foo operator+(int, const Foo); //D extern(C++) struct struct Foo { Foo opBinary!+(const ref Foo); //??? I see... } Foo opBinary!+(int, const ref Foo); //??? But this would of course be opBinaryRight, and inside struct Foo. What if Foo operator+(const Bar, const Foo);? Is it Foo.opBinaryRight, or Bar.opBinary, or both?
C++ overloaded operators and D
Now D provides very powerfull means to link C++ code with D. However D doesn't allow to call C++ overloaded operators. It's very annoying, because C++ code may don't provide non-operator analogues. What we know about C++ overloadable operators? Overloaded operator in C++ is a trivial function/method with special name. Thus operator[](int) differs from op_index(int) function only by mangle. C++ OO have a different behaviour from D OO (for example C++ allows different and operator overloads or static function fro binary operators), thus we should avoud the temptation of map C++ OOs to D OOs, or back. Also D provides a pragma(mangle) which allows to redefine symbol mangle. It takes a string argument and redefine mangle to it: pragma(mangle, foo) void bar();//bar.mangleof == foo I suggest to modify pragma(mangle) to support C++ operators. If argument of this pragma is identifier (for example cppOpAdd), the pragma applied to extern(C++) function or method, compiler mangle the function in accordance with this identifier. //C++ struct Foo { int operator[](int); //another fields }; //D extern(C++) struct Foo { pragma(mangle, cppOpIndex) ref int op_index(int); //another fields } //using: Foo f; f.op_index(1)++; //OK, op_index is linked with Foo::operator[] f[1]++; //Error, no special behaviour for op_index I think this approach is simple, doesn't modify the language, can be easily implemented and usefull. Destroy!
D hackers requested. Dancing around postblit.
Please try to solve this task: We have a struct S. S has a some kind of postblit (user-defined or inherited frome field) and destructor. S has a @disabled opAssign. We have an unitialized allocated memory block whick mapped to a S array. struct S { //postblit and dtor here } S[] data; // contains a garbarge void emplace()(ref S val, size_t i); The task: we should write emplace function, which initializes i-th element of data with val; This function shouldn't call any dtors for example for the old data[i] (which contains a garbarge). This function should be transparent for attributes. For example, if S postblit is nothrow emplace should be nothrow too. If S postblit is not nothrow, S should't be nothrow. This function should call postblit only one time and shouldn't call any other S special functions (opAssign, ctor etc.)
Re: D hackers requested. Dancing around postblit.
On Wednesday, 5 November 2014 at 20:01:59 UTC, Adam D. Ruppe wrote: If you're willing to make it @system or @trusted, you can just copy the data and call postblit manually: extern(C) @safe int printf(in char*); struct S { ~this() { printf(dtor\n); } this(this) { printf(postblit\n); } @disable S opAssign(S s); } S[] data; void emplace(Struct)(ref Struct val, size_t i) { // just blit the struct contents over // (same thing normal assign does anyway) (cast(ubyte*)(data[i]))[0 .. Struct.sizeof] = (cast(ubyte*)val)[0 .. Struct.sizeof]; // call postblit data[i].__postblit(); } void main() { data.length = 1; S s; emplace(s, 0); } Since it is a template, attribute inference should take care of nothrow, etc. It was my first try :( data[i].__postblit(); calls the user-defined postblit and only it. struct Sx { ~this() { printf(dtor\n); } this(this) { printf(postblit\n); } @disable S opAssign(S s); } struct S { Sx s; } data[i].__postblit(); //error: S hasn't __postblit member, but it need the postblit call for Sx typeid(S).postblit(data[i]) works as needed, but doesn't save postblit attributes.
Re: D hackers requested. Dancing around postblit.
FYI: My second try: void emplace(V1)(ref V1 dst, ref V1 src) @trusted if (is(V1 == struct)) { static if (new_obj) { V1 tmp = cast(V1)src; //create copy and call postblit static V1 init = V1.init; //bitwise copy of object, which already postblitted (cast(void*)dst)[0 .. V1.sizeof] = (cast(void*)tmp)[0 .. V1.sizeof]; //initialize tmp with V1.init. (cast(void*)tmp)[0 .. V1.sizeof] = (cast(void*)init)[0 .. V1.sizeof]; } } This method do one extra dtor call for tmp, but it may be trivial because tmp is V.init. However it is not a complete solution of the task.
Re: D hackers requested. Dancing around postblit.
On Wednesday, 5 November 2014 at 20:33:40 UTC, Adam D. Ruppe wrote: On Wednesday, 5 November 2014 at 20:19:03 UTC, IgorStepanov wrote: data[i].__postblit(); //error: S hasn't __postblit member, but it need the postblit call for Sx Hmm, we could perhaps do a recursive call with __traits(allMembers) and if it has a __postblit. Good point. I'll try it. Thanks.
Re: DIP66 v1.1 (Multiple) alias this.
* At the AliasThis declaration semantic stage, the compiler can perform the initial checks and reject the obviously incorrect AliasThis declarations. - it might be simpler (for the sake of simplifying generic code) to just delay all error checking to the first use. I disagree with that. Current check is not recursive and prevent you code from a silly errors: struct X(T, V) { T t; V v; alias t this; alias v this; //Error if is(T == V). However this code is fundamentally broken, and this error should be raised as soon as possible. } The code is not fundamentally broken if alias this is never used. I agree rejecting the code compulsively is also sensible, ONLY if there is a simple way to write a static if condition to make the code work. Meaning: struct X(T, V) { T t; V v; static if (please_fill_this) alias t this; static if (please_fill_this_too) alias v this; } If the two conditions are too hard to write then it would be difficult to argue this point successfully. This code can be rewritten as: struct X(T, V) { T t; V v; alias t this; static if (!is(V == T)) alias v this; } The code is not fundamentally broken if alias this is never used. I meant that when you say that X is a subtype of T and X is a subtype of V where you don't know what T and V are, it means you don't really know what you're doing. And that is an error and the compiler should inform you about it as soon as possible. However I may be mistaken. Understood. All: okay to make alias this + opDispach applicable to the same expression an error? I think it will be nice. I'm sending this now with these points, will make one more pass through the DIP when I'm online again. Ok, I'll wait. And please, answer the question about the is-expression.
Re: DIP66 v1.1 (Multiple) alias this.
On Monday, 3 November 2014 at 20:06:27 UTC, Marc Schütz wrote: On Monday, 3 November 2014 at 15:39:42 UTC, IgorStepanov wrote: I meant that when you say that X is a subtype of T and X is a subtype of V where you don't know what T and V are, it means you don't really know what you're doing. And that is an error and the compiler should inform you about it as soon as possible. However I may be mistaken. IMO the behaviour should be analogous to name lookup for modules: there should be an error only on use. It's hard to come up with a non-artificial example, but I can imagine there are some valid use cases in generic code. It won't hurt to report the ambiguity error on use, while it could theoretically hurt to report it early, so I'd suggest to go with the former. There are two cases: 1: when alias a this tries to override base class typeof(a) 2: when alias a this tries to override alias b this where is(typeof(a) == typeof(b)) The first check is hard to implement at lookup-time, because base classes are resolved before alias this. The second check may be easely dropped (anyway alias this conflicts are resolved properly at lookup time). Do you accept this scheme (remove the second check but still alive the first check)?
DIP66 v1.1 (Multiple) alias this.
http://wiki.dlang.org/DIP66 I've applied some changes to it, however there are still some unresolved questions. Here's my destruction: * symbol can be a field or a get-property (method annotated with @property and taking zero parameters). - actually: (a) the @property annotation is not necessary (b) there may be one ore more parameters so long as they're all defaulted So the text should be obj.symbol must be a valid expression. Done. * At the AliasThis declaration semantic stage, the compiler can perform the initial checks and reject the obviously incorrect AliasThis declarations. - it might be simpler (for the sake of simplifying generic code) to just delay all error checking to the first use. I disagree with that. Current check is not recursive and prevent you code from a silly errors: struct X(T, V) { T t; V v; alias t this; alias v this; //Error if is(T == V). However this code is fundamentally broken, and this error should be raised as soon as possible. } class A : B { B b; alias b this; //Error: super type (B) always hides aliasthised type B because base classes should be processed before alias this types. } * I don't think the pseudocode helps a lot. Better let's have a clear and precise specification (I've edited the lookup order into an ordered list). Done. * Regarding the lookup, opDispatch shouldn't come before alias this, or should come before base class lookup. Essentially alias this is subtyping so it should enjoy similar privileges to base classes. A different way to look at it is opDispatch is a last resort lookup mechanism, just one step above the UFCS lowering. I agree with this suggestion, however it breaks an existing code. opDispatch shouldn't come before base type lookup, because it will hide basic methods like toString. opDispatch may come after alias this lookup, however it will fundamentally change program behaviour. Current (implemented is released compiler) behaviour: import std.stdio; struct Base { string foo() { return Base.foo; } } struct Derived { Base b; alias b this; string opDispatch(string s)() { return opDispatch; } } void main() { Derived d; writeln(d.foo()); //prints opDispatch } After your change this call will write Base.foo. And I see no way to add third transitional state to allow users rewrite those code correctly. This changing will suddenly, without any warning, change a user code. I understand that this case is very rare however it may be. And, TBH, this issue not relevant with multiple alias this :-) * The DIP should specify the working of alias this as rewrites/lowerings, not pseudocode. Basically for each kth declaration alias symbolk this; the compiler rewrites obj.xyz as obj.symbolk.xyz and then does the usual lookup on that expression. That means the whole algorithms is applied again etc. If more than one rewrite typechecks, that's an ambiguity error. Ok. I've removed pseudocode. Is it better now? * IMPORTANT: The DIP must discuss rvalue vs. lvalue cases. The rewrite approach simplifies that discussion because it's clear what happens by simply reasoning about the rewritten expression. Lvalue vs. rvalue matters a lot practically. Consider: struct A { private int x; alias x this; } struct B { private int _x; int x() { return x; } alias x this; } Then x can be passed by reference, modified directly etc. for A but not for B. Done. I've added corresponding chapter to the DIP and commit to the PR.
Re: DIP66 v1.1 (Multiple) alias this.
And there is dispute about is expression: see http://forum.dlang.org/thread/ubafmwvxwtolhmnxb...@forum.dlang.org?page=5
Re: DIP66 - Multiple alias this
On Tuesday, 28 October 2014 at 23:12:21 UTC, Meta wrote: On Tuesday, 28 October 2014 at 22:55:24 UTC, IgorStepanov wrote: You may see isFloatingPoint declaration in traits.d: enum bool isFloatingPoint(T) = is(FloatingPointTypeOf!T) !isAggregateType!T; This template explicitly says that T shouldn't be an aggregate type. Thus std.math.isNaN(X)(X x) if (isFloatingPoint!X) shouldn't accept a struct. Although alias this is supposed to denote subtyping, here is a violation of the Liskov Substitution Principle; although S is a subtype of float, there are some cases where it's invalid to substitute an S for a float. This seems like a problem with alias this to me. As S is aliased to float, typeof(s.val) should be passed to isFloatingPoint if passing S fails. Your issue is not relevant with alias this implementation. isFloatingPoint is a library implemented trait, which uses compile-time reflections to for verifying the truth of the approval T is floating point type. If you think that isFloatingPoint implemented incorrectly, you may start corresponding discussion. Otherwice, if you think that std.math.isNaN should accept user types which may be converted to floating point type, you may discuss that. If you want simply solve you problem you may define own auto isNaN(T)(S!(T) arg) { return isNaN(cast(T)arg); }
Re: DIP66 - Multiple alias this
On Tuesday, 28 October 2014 at 02:07:23 UTC, Andrei Alexandrescu wrote: On 10/24/14 6:05 AM, IgorStepanov wrote: On Friday, 24 October 2014 at 06:04:24 UTC, Andrei Alexandrescu wrote: On 10/19/14 2:00 PM, IgorStepanov wrote: Bump. I've made a few grammar and fluency edits to the DIP, and collected a few thoughts while doing that. Will get back on this before too long. -- Andrei I've seen it. Thanks! Waiting for a comments. Coming soon. I've been under quite a bit of pressure as of late. Should I add chapter about method overloading with alias this: it should work (and works) as cross-module overloading. It should imply This implies from the DIP but hasn't written explicitly. Interesting. I think it should work like overloading of calls in base and derived classes. Not really. Rules which define overloading class methods doesn't describe multiple inheritance case. In single inheritance case alias this rule is similar with inheritance rule: derived overload set hides base base overload set: struct A { void foo(string) { writeln(string); } void foo(int) { writeln(int); } } struct B { void foo(double) { writeln(double); } A a; alias a this; } B b; b.foo(string); //error b.foo(42); //called B.foo(double), not B.a.foo(int); The second test is not similar to inherintance rules, but this case is not relevant with multiple alias this and it is already done. When I said cross-module overloading, I told about case when C inherits (via alias this) A and B, both A and B have foo methods with different paramethers and compiler should resolve foo call: struct A { void foo(string) { writeln(string); } void foo(int) { writeln(int); } } struct B { void foo(double) { writeln(double); } } struct C { A a; B b; alias a this; alias b this; } C c; c.foo(str); // OK, c.a.foo(string) c.foo(4.2); //OK, c.b.foo(double); c.foo(42); //Error: c.a.foo(int) or c.b.foo(double)? BTW, similar case with multiple inheritance via interfaces works absolutely incorrect: interface CA { final void foo(string) { writeln(string); } final void foo(int) { writeln(int); } } interface CB { final void foo(double) { writeln(double); } } class CC : CA, CB { } auto cc = new CC(); cc.foo(xxx); //OK, called CA.foo(string) cc.foo(42); //called CA.foo(int) without any warnings cc.foo(4.2); //Error: unable to call CA.foo with double argument. In other words, compiler choses the first base interface, uses its overload set and ignores other interfaces. OT: Should `et\s?c\.?` be written as etc in English? I thought that it should be written as et c., because et cetera. Or is it an anachronism? You can't go wrong with M-W: http://www.merriam-webster.com/dictionary/etc Thanks.
Re: DIP66 - Multiple alias this
On Tuesday, 28 October 2014 at 19:45:09 UTC, Andrei Alexandrescu wrote: On 10/10/14 10:09 AM, IgorStepanov wrote: I've created DIP for my pull request. DIP: http://wiki.dlang.org/DIP66 PR: https://github.com/D-Programming-Language/dmd/pull/3998 Please, comment it. Here's my destruction: * symbol can be a field or a get-property (method annotated with @property and taking zero parameters). - actually: (a) the @property annotation is not necessary (b) there may be one ore more parameters so long as they're all defaulted So the text should be obj.symbol must be a valid expression. * At the AliasThis declaration semantic stage, the compiler can perform the initial checks and reject the obviously incorrect AliasThis declarations. - it might be simpler (for the sake of simplifying generic code) to just delay all error checking to the first use. * I don't think the pseudocode helps a lot. Better let's have a clear and precise specification (I've edited the lookup order into an ordered list). * Regarding the lookup, opDispatch shouldn't come before alias this, or should come before base class lookup. Essentially alias this is subtyping so it should enjoy similar privileges to base classes. A different way to look at it is opDispatch is a last resort lookup mechanism, just one step above the UFCS lowering. * The DIP should specify the working of alias this as rewrites/lowerings, not pseudocode. Basically for each kth declaration alias symbolk this; the compiler rewrites obj.xyz as obj.symbolk.xyz and then does the usual lookup on that expression. That means the whole algorithms is applied again etc. If more than one rewrite typechecks, that's an ambiguity error. * IMPORTANT: The DIP must discuss rvalue vs. lvalue cases. The rewrite approach simplifies that discussion because it's clear what happens by simply reasoning about the rewritten expression. Lvalue vs. rvalue matters a lot practically. Consider: struct A { private int x; alias x this; } struct B { private int _x; int x() { return x; } alias x this; } Then x can be passed by reference, modified directly etc. for A but not for B. === Congratulations on taking this to a DIP. It clarifies things really nice and it's an example to follow for future language changes. Andrei Thanks for answer, I hope, I'll able to to carefully consider your comments tomorrow. Now I want to ask about the one thing. What do you think about compatibility with existing alias this implementation? For example you wrote: Regarding the lookup, opDispatch shouldn't come before alias this, or should come before base class lookup.. This requirement breaks the existing code. We can apply this change in DIP, however if we want to apply this change to compiler, we should be careful. Moreover, I don't see the way to create deprecation path: old implementation-deprecated old implementation new inmplementation - prohibited old implementation new inmplementation. If we will change the semantic order of proprerty resolving, we will not able to warn user about changes. Suddenly his code will be compiled in a different way. And please comment my way to resolving is expression via alias-this: http://forum.dlang.org/thread/ubafmwvxwtolhmnxb...@forum.dlang.org?page=5 Thanks for the DIP review.
Re: DIP66 - Multiple alias this
On Tuesday, 28 October 2014 at 21:55:35 UTC, Meta wrote: On Tuesday, 28 October 2014 at 20:09:07 UTC, IgorStepanov wrote: And please comment my way to resolving is expression via alias-this: http://forum.dlang.org/thread/ubafmwvxwtolhmnxb...@forum.dlang.org?page=5 Something else related to the discussion about `is` from this thread: http://forum.dlang.org/post/tulvmydnogbgebnxy...@forum.dlang.org. The following code: import std.math; import std.traits; struct S(T) if(isFloatingPoint!T) { T val; alias val this; } void main() { auto s = S!float(); assert(isNaN(s)); s = 10.0; assert(!isNaN(s)); } Current fails to compile with this error: Error: template std.math.isNaN cannot deduce function from argument types !()(S!float), candidates are: std/math.d(4171):std.math.isNaN(X)(X x) if (isFloatingPoint!X) Is this a problem with the current implementation of `alias this`, or should isFloatingPoint be changed so that it also accepts types that alias a floating point type? You may see isFloatingPoint declaration in traits.d: enum bool isFloatingPoint(T) = is(FloatingPointTypeOf!T) !isAggregateType!T; This template explicitly says that T shouldn't be an aggregate type. Thus std.math.isNaN(X)(X x) if (isFloatingPoint!X) shouldn't accept a struct.
Re: DIP66 - Multiple alias this
On Friday, 24 October 2014 at 06:04:24 UTC, Andrei Alexandrescu wrote: On 10/19/14 2:00 PM, IgorStepanov wrote: Bump. I've made a few grammar and fluency edits to the DIP, and collected a few thoughts while doing that. Will get back on this before too long. -- Andrei I've seen it. Thanks! Waiting for a comments. Should I add chapter about method overloading with alias this: it should work (and works) as cross-module overloading. It should imply This implies from the DIP but hasn't written explicitly. OT: Should `et\s?c\.?` be written as etc in English? I thought that it should be written as et c., because et cetera. Or is it an anachronism?
Re: DIP66 - Multiple alias this
On Friday, 24 October 2014 at 13:17:28 UTC, Meta wrote: On Friday, 24 October 2014 at 13:05:54 UTC, IgorStepanov wrote: On Friday, 24 October 2014 at 06:04:24 UTC, Andrei Alexandrescu wrote: On 10/19/14 2:00 PM, IgorStepanov wrote: Bump. I've made a few grammar and fluency edits to the DIP, and collected a few thoughts while doing that. Will get back on this before too long. -- Andrei I've seen it. Thanks! Waiting for a comments. Should I add chapter about method overloading with alias this: it should work (and works) as cross-module overloading. It should imply This implies from the DIP but hasn't written explicitly. OT: Should `et\s?c\.?` be written as etc in English? I thought that it should be written as et c., because et cetera. Or is it an anachronism? The convention is to write etc., with the period indicating that the rest of the word has been omitted. If it appears in the middle of a sentence, I don't capitalize the subsequent word as it doesn't really make sense, but I'm not sure what the actual convention is in that respect. Thanks for the explanation:)
Re: DIP66 - Multiple alias this
On Tuesday, 21 October 2014 at 08:17:19 UTC, Dicebot wrote: On Sunday, 19 October 2014 at 21:00:29 UTC, IgorStepanov wrote: Bump. For me current description and PR look solid and safe enough to try. We are waiting for an Andrey's Word. Walter, as I understand haven't unresolved objections.
Re: DIP66 - Multiple alias this
Bump.
Re: DIP66 - Multiple alias this
On Monday, 20 October 2014 at 00:23:46 UTC, Daniel N wrote: On Wednesday, 15 October 2014 at 09:50:17 UTC, IgorStepanov wrote: In first edition I've implemented rule, when if type defines alias this directly, this alias hides all indirect aliases with the same type. However Andrey said that we should implement the strictest rules as possible and maybe relax them later. Thus I implemented current rules, but saved the old implemetation of search. After some time we will able to return to first rule. It's not hard. I see, in order to prevent any accidental shadowing, maybe one can consider override alias this, if we decide to make a relaxed version in the future? Make sense. I think we should postpone but not forgotten this feature. When main part will be merged, we will able to start discussion about override alias this. What about the other features?
Re: Postblit bug
On Friday, 17 October 2014 at 14:18:31 UTC, monarch_dodra wrote: On Friday, 17 October 2014 at 00:55:25 UTC, ketmar via Digitalmars-d wrote: On Fri, 17 Oct 2014 00:42:24 + IgorStepanov via Digitalmars-d digitalmars-d@puremagic.com wrote: Can someone comment this code? Should I think that it's a bug. it's just an anomaly. const postblit can do alot of things besides adjusting struct fields, and it's logical that compiler cannot call non-const methods for const objects. yet it's still on of those unforseen consequences that arises from conjunction of different features. i don't think that it's a bug, but i think that this must be discussed anyway, and then documented. AFAIK, Kenji has submitted a DIP, and has begun working on fixing the const/immutable/inout posblit issue. However, there are some very subtle corner cases, so (afaik) work is slow. To be honest, I think people use const way too much in D. It's *not* the C++ head const you can use anywhere. It's really just the base attribute between mutable and immutable data. In particular, due to the transitive nature of const, any time you use const it means you can't modify this, or anything produced or acquired from this, ever. It's usually not what people think they are signing for... When it makes little sense to have your type as immutable, then I don't think you should bother much What happends if we will ignore const/immutable modifier for postblits? Is it create any holes?
Re: Postblit bug
On Friday, 17 October 2014 at 15:20:50 UTC, ketmar via Digitalmars-d wrote: On Fri, 17 Oct 2014 14:41:36 + IgorStepanov via Digitalmars-d digitalmars-d@puremagic.com wrote: What happends if we will ignore const/immutable modifier for postblits? Is it create any holes? it will break const promise. i.e. const/immutable data is not really immutable now, it can be modified. It's just common words=) I meant that when postblit is called when new object is being creating and doesn't exists for user code. E.g. const S v1 = v2; Ok, v1 _will_ be const when it will be _created_. However postblit can think that object is mutable, because it called before the first accessing to the object from user code. Thus I ask about case when postblit may mutate a const object, which created before postblitted object and may been accessed from user code before this postblitting.
Re: Postblit bug
On Friday, 17 October 2014 at 17:25:47 UTC, monarch_dodra wrote: On Friday, 17 October 2014 at 16:19:47 UTC, IgorStepanov wrote: It's just common words=) I meant that when postblit is called when new object is being creating and doesn't exists for user code. E.g. const S v1 = v2; Ok, v1 _will_ be const when it will be _created_. However postblit can think that object is mutable, because it called before the first accessing to the object from user code. Thus I ask about case when postblit may mutate a const object, which created before postblitted object and may been accessed from user code before this postblitting. That's way too many words for a single sentence for me to understand ;) Yes, my english is so bad:) But maybe this answers your question? Yes, I've understood. TBH, I have got this error while I am working on new AA, and I want to fix it as posdible faster (one way or another). I have the next code: struct AssociativeArray(Key, Value) { ... struct Entry { Key key; Value value; ... } } If key or value is a const struct with postblit I have got a error. I don't need an any postblit in Entry. Thus I suggest another solution: Do not generate helper functions like __fieldPostBlit, if struct has a @disabled this(this); Destroy it.
Re: Postblit bug
On Friday, 17 October 2014 at 19:45:43 UTC, ketmar via Digitalmars-d wrote: On Fri, 17 Oct 2014 19:39:39 + IgorStepanov via Digitalmars-d digitalmars-d@puremagic.com wrote: Thus I suggest another solution: Do not generate helper functions like __fieldPostBlit, if struct has a @disabled this(this); Destroy it. `@disable this (this);` means that struct can't be copied. it's irrelevant what code compiler generates behind our backs, it will not be executed anyway, 'cause compiler will complain: Error: struct XXX is not copyable because it is annotated with @disable. This error will be raised if I try to copy the my struct. But I don't want to do it. Now the error raised when I define the struct.
Postblit bug
I've found a strange postblit bug (or not a bug?): struct A { this(this) { } } struct B { A a; } struct C { const B b; } void main() { C c; } When I try to compile it, compiler raises the error: (Error: mutable method testaa2.B.__fieldPostBlit is not callable using a const object) If I mark this(this) as const (BTW, is this(this) const a correct definition?) error still raises. When I tried to reduce this code, I've found the next: struct A { this(this) { } } struct B { const A a; } void main() { B b; } Error: mutable method testaa2.A.__postblit is not callable using a const object If I change this(this) to const, error has been lost. struct A { this(this) const { } } struct B { const A a; } void main() { B b; } Can someone comment this code? Should I think that it's a bug.
Re: Postblit bug
On Friday, 17 October 2014 at 00:55:25 UTC, ketmar via Digitalmars-d wrote: On Fri, 17 Oct 2014 00:42:24 + IgorStepanov via Digitalmars-d digitalmars-d@puremagic.com wrote: Can someone comment this code? Should I think that it's a bug. it's just an anomaly. const postblit can do alot of things besides adjusting struct fields, and it's logical that compiler cannot call non-const methods for const objects. yet it's still on of those unforseen consequences that arises from conjunction of different features. i don't think that it's a bug, but i think that this must be discussed anyway, and then documented. I think, this is unexpected behaviour: Compiler generates __fieldPostBlit for all structs, and call it when postblit is needed. In this example compiler generates something like the next code: struct A { this(this) { } } struct B { A a; void __fieldPostBlit() { a.__postblit(); //a has an explicit postblit } } struct C { const B b; void __fieldPostBlit() { b.__fieldPostBlit(); //Error: b is const } } void main() { C c; C c2 = c; //= //memcpy(c2, c, C.sizeof) //c2.__fieldPostBlit(); } However, __postblit and __fieldPostBlit are always called for new object, thus it can neglect const guarantee and generate postblits like: void __fieldPostBlit() { (cast()b).__fieldPostBlit(); }
Re: DIP66 - Multiple alias this
On Wednesday, 15 October 2014 at 03:49:41 UTC, Daniel N wrote: On Wednesday, 15 October 2014 at 02:46:05 UTC, Dicebot wrote: On Tuesday, 14 October 2014 at 12:33:50 UTC, IgorStepanov wrote: This code tell that C is subtype of A and C is subtype of B. User can use this fact in his code: void foo(B); C c = new C; foo(c); //Ok. Of course, we shouldn't allow user to cast c to int: int i = c; //wrong However, user can explicitly cast c to his subtype, which is convertable to int: int i = cast(B)c; //Ok Summarizing, I disagree with suggestion disallow this code at type semantic stage. I agree. It will also make possible to break already working disambugation of `foo(c)` kind but adding new `alias this` to one of subtypes independently. That sounds annoying. I guess the best part of D is, you have the means to fix anything you disagree with yourself... I can add a static assert to my class and be happy. I have another idea, we could define that the shortest conversion chain wins, analogous to type promotions, that makes it possible to contain the issue inside C. class C { A a; B b; int disambiguate_int() { return a; } alias a this; alias b this; alias disambiguate_int this; static assert(__traits(compiles, {int _ = C.init;}), Ambiguous alias this); } i.e. this assert should pass. In first edition I've implemented rule, when if type defines alias this directly, this alias hides all indirect aliases with the same type. However Andrey said that we should implement the strictest rules as possible and maybe relax them later. Thus I implemented current rules, but saved the old implemetation of search. After some time we will able to return to first rule. It's not hard.
Re: DIP66 - Multiple alias this
On Monday, 13 October 2014 at 15:21:32 UTC, Daniel N wrote: On 10/11/2014 7:23 AM, IgorStepanov wrote: class A { int i; alias i this; } class B { int i; alias i this; } class C { A a; B b; alias a this; alias b this; } My preferred solution would be to reject the 2nd alias declaration outright. I don't see any value in intentionally creating the above pattern, _if_ it occurs then it's most likely due to an unintentional side-effect of a re-factoring, thus it should error out as close as possible to the real error. This code tell that C is subtype of A and C is subtype of B. User can use this fact in his code: void foo(B); C c = new C; foo(c); //Ok. Of course, we shouldn't allow user to cast c to int: int i = c; //wrong However, user can explicitly cast c to his subtype, which is convertable to int: int i = cast(B)c; //Ok Summarizing, I disagree with suggestion disallow this code at type semantic stage.
Re: DIP66 - Multiple alias this
On Monday, 13 October 2014 at 00:54:13 UTC, Steven Schveighoffer wrote: On 10/12/14 7:16 PM, IgorStepanov wrote: On Sunday, 12 October 2014 at 23:02:13 UTC, Steven Schveighoffer wrote: On 10/10/14 6:10 PM, IgorStepanov wrote: You can write foo(c.shadow); This isn't hard. Ok, I understood you, let's listen to what others say Right, you can get around it. But the issue here is, that I feel like is(T: U) means (from dlang.org): is ( Type : TypeSpecialization ) The condition is satisfied if Type is semantically correct and it is the same as or can be implicitly converted to TypeSpecialization. This means is(C : int) should indicate that C can implicitly convert to int. But in your DIP, it does not. I think this is incorrect. Hmm. I've written case (my previous post), when returning false from is(T: S), where T has many pathes to S is dangerous. OK, I didn't understand your case before, but I just got it. I understand what you mean, but this isn't anything new -- one can cause weird problems by creating diamond-pattern interfaces also. I do not actually think it is dangerous, because one would not leave an error call in their code. So for a future change to a library to mystically make a function start working is not a danger, because said code wasn't sitting there broken in the first place. I will note, that for diamond problem interfaces, the compiler seems to take a different track than your DIP: interface A {} interface B : A {} interface C : A {} class X : B, C {} static assert(is(X : A)); void main() { A a = new C; // works, not sure if it's B.A or C.A } I know this is a different problem -- we aren't pointing at two different concrete implementations. -Steve This is fundamentally different situation: interfaces haven't a state, thus don't care what interface will be getted: B.C or C.C. Moreover, we can think that we have only one base C (like virtual inherited class in C++). Alias this case requires a completely different approach.
Re: DIP66 - Multiple alias this
On Sunday, 12 October 2014 at 08:36:05 UTC, Marc Schütz wrote: On Sunday, 12 October 2014 at 04:31:22 UTC, Walter Bright wrote: On 10/11/2014 7:23 AM, IgorStepanov wrote: class A { int i; alias i this; } class B { int i; alias i this; } class C { A a; B b; alias a this; alias b this; } void foo(T)(T arg) if(is(T : int)) { ... } foo(C()); //Should it pass or not? There's a rule with imports that if the same symbol is reachable via multiple paths through the imports, that it is not an ambiguity error. Here, the same type is reachable through multiple alias this paths, so by analogy it shouldn't be an error. It's the same type, but different symbols; actual accesses would be ambiguous. `is(T : int)` shouldn't evaluate to true if `int a = T.init;` would fail. I found an example of a situation that is bothering me. Let we have a persistence framework, which provides a storing D object in some persistence storage: DB, file et c. In introduces paired functions store/load and special type PersistenceObject. If stored type is subtype of PersistenceObject it converts to PersistenceObject and PersistenceObject.load(stream) called for loading object (and PersistenceObject.store(stream) for storing). Otherwice if object can't be converted to PersistenceObject it should be serialized via serialize function (or deserialized via deserialize). struct PersistenceFramework { void store(T)(T arg) if (is(T : PersistenceObject)) { PersistenceObject po = arg; arg.store(stream); } void store(T)(T arg) if (!is(T : PersistenceObject)) { PersistenceObject po = arg; store(serialize(arg)); } void load(T)(ref T arg) if (is(T : PersistenceObject)) { PersistenceObject po = arg; arg.load(stream); } void load(T)(ref T arg) if (!is(T : PersistenceObject)) { PersistenceObject po = arg; load(serialize(arg)); } Stream stream; } / And we have the next types which we want to store and load */ struct Role { ... } struct User { Role role; PersistenceObject po; //... alias role this; alias po this; } /*/ User u; persistenceFramework.load(u) //... persistenceFramework.store(u); /**/ Role is not subtype of PersistenceObject thus all works ok. We can store User via User.po and load it again; Some time later, Role designer decided that Role should be subtype of PersistenceObject and changed Role definition: struct Role { ... PersistenceObject po; alias po this; } Now, User can not be converted to PersistenceObject because there are two path to convert: User.po and User.role.po; Storing code after this change will be copiled successfully (if we follow your is rule), however object will be tried to load via void load(T)(ref T arg) if (!is(T : PersistenceObject)). Because object was saved via void store(T)(T arg) if (is(T : PersistenceObject)) at the previous program run, user will not be loaded succesfully. Moreover, you will get an strange unexpected program behaviour and will be hard to find real error cause. /*/ And finally, if you want to check, if you Type _really_ can be converted to AnotherType, you can use the next check: void foo(Type)(Type arg) if (is(typeof({AnotherType x = Type.init;}))) { }
Re: DIP66 - Multiple alias this
On Sunday, 12 October 2014 at 23:02:13 UTC, Steven Schveighoffer wrote: On 10/10/14 6:10 PM, IgorStepanov wrote: On Friday, 10 October 2014 at 21:26:49 UTC, Steven Schveighoffer wrote: An example: foo(T)(T t) if(is(T : int)) { someFuncThatTakesInt(t); } foo(T)(T t) if(!is(T : int) is(T.shadow : int)) { someFuncThatTakesInt(t.shadow); } struct A { int i; alias i this; } struct B { int i; alias i this; } struct C { A a; B shadow; alias a this; alias shadow this; } C c; foo(c); // should compile, but I think your DIP makes it fail due to ambiguity You can write foo(c.shadow); This isn't hard. Ok, I understood you, let's listen to what others say Right, you can get around it. But the issue here is, that I feel like is(T: U) means (from dlang.org): is ( Type : TypeSpecialization ) The condition is satisfied if Type is semantically correct and it is the same as or can be implicitly converted to TypeSpecialization. This means is(C : int) should indicate that C can implicitly convert to int. But in your DIP, it does not. I think this is incorrect. -Steve Hmm. I've written case (my previous post), when returning false from is(T: S), where T has many pathes to S is dangerous. However your words also contain the truth. I don't know what we need to do. Maybe we should raise error during is semantic? Please, read my example and say your opinion.
Re: DIP66 - Multiple alias this
Advantage of ky way is a more strictness then your way: if function with if(is(T: S)) will be called, error will be raised at the first trying of convert T to S. And we don't give the opportunity of possible error to spread away from the place of origin.
Re: DIP66 - Multiple alias this
On Saturday, 11 October 2014 at 00:00:48 UTC, Walter Bright wrote: On 10/10/2014 4:23 PM, IgorStepanov wrote: On Friday, 10 October 2014 at 22:50:25 UTC, Walter Bright wrote: must be an error, even if one of C.a() or C.b() might be a better match. This is how things work for template mixins and imports. So it is. Good! The same rule applies for overloading. I've implemented overloading: https://github.com/D-Programming-Language/dmd/pull/3998/files#diff-17b22eae29e74ce6ec29037438b5031cR2136 Please, tell me, what changes should I make to the DIP as a result of yesterday's discussions. And please, tell your opinion about is issue: class A { int i; alias i this; } class B { int i; alias i this; } class C { A a; B b; alias a this; alias b this; } void foo(T)(T arg) if(is(T : int)) { ... } foo(C()); //Should it pass or not?
DIP66 - Multiple alias this
I've created DIP for my pull request. DIP: http://wiki.dlang.org/DIP66 PR: https://github.com/D-Programming-Language/dmd/pull/3998 Please, comment it.
Re: DIP66 - Multiple alias this
On Friday, 10 October 2014 at 17:31:23 UTC, Steven Schveighoffer wrote: On 10/10/14 1:09 PM, IgorStepanov wrote: I've created DIP for my pull request. DIP: http://wiki.dlang.org/DIP66 PR: https://github.com/D-Programming-Language/dmd/pull/3998 Please, comment it. Hm... not sure you need a DIP. From page 231 of TDPL: A class could introduce any number of alias this declarations, thus subtyping any number of types. -Steve TDPL tells that multiple alias this should be allowed, but tell nothing about conflict resolving. General idea of this DIP is sistematize rules for alias this. Please comment this part. Maybe I've forgot some cases, maybe introduce dangerous semantic rule.
Re: DIP66 - Multiple alias this
Which will now create an error in the wrong place. IMO, the 'is' test should also fail. -Steve In this case, you will see the real error and will able to fix if. For example call foo(c.a); Otherwice, you possible error will be fixed without taking into account your opinions.
Re: DIP66 - Multiple alias this
On Friday, 10 October 2014 at 20:29:43 UTC, Brian Schott wrote: On Friday, 10 October 2014 at 17:09:08 UTC, IgorStepanov wrote: I've created DIP for my pull request. DIP: http://wiki.dlang.org/DIP66 PR: https://github.com/D-Programming-Language/dmd/pull/3998 Please, comment it. There is an error in the wiki formatting in the second code block. (`{| class=wikitable` should not be there) I don't see any problems with the actual content of the DIP. Fixed. This is starnge implicit copy-paste of my text editor =/
Re: DIP66 - Multiple alias this
On Friday, 10 October 2014 at 20:47:45 UTC, Steven Schveighoffer wrote: On 10/10/14 1:09 PM, IgorStepanov wrote: I've created DIP for my pull request. DIP: http://wiki.dlang.org/DIP66 PR: https://github.com/D-Programming-Language/dmd/pull/3998 Please, comment it. This part: void test() { C c; int i = c; //Error: c.a.i vs c.b.i } static assert(is(C : int)); //Ok, because C is subtype of int anyway. I think might be wrong. There is a lot of code out there that says, e.g.: void foo(T)(T t) if(is(T : U)) { U u = t; ... } Which will now create an error in the wrong place. IMO, the 'is' test should also fail. -Steve I thought exactly about this using case. See: You have a struct like this in first place: struct A { int i; alias i this; } struct C { A a; string s; alias a this; alias s this; } And you have a template function in second place: void foo(T)(T t) if(is(T : int)) { ... } void foo(T)(T t) if(is(T : string)) { ... } And you have the code it third place: C c; foo(c); //Error: what do you mean: foo!(T : string) or foo!(T : int) Now, someone (A developer) changed the A definition: struct A { int i; alias i this; } struct B { int i; alias i this; } struct C { A a; B b; string s; alias a this; alias b this; alias s this; } And now, you code mystically start to works. Attention: Infusing in one place conflict resolves conflict in another place. It is danger, I think.
Re: DIP66 - Multiple alias this
On Friday, 10 October 2014 at 20:44:11 UTC, Marc Schütz wrote: On Friday, 10 October 2014 at 17:09:08 UTC, IgorStepanov wrote: I've created DIP for my pull request. DIP: http://wiki.dlang.org/DIP66 PR: https://github.com/D-Programming-Language/dmd/pull/3998 Please, comment it. I understand that as a first step it was suggested to implement the strictest behaviour regarding conflicts, namely to disallow them entirely. However, I think more permissive strategies are useful, like in this example: https://github.com/D-Programming-Language/dmd/pull/3998#issuecomment-58570742 Conflict resolution can work like overload resolution, with different levels of matching and partial ordering. There isn't hard to change resolving strategy. This will need to change one function definition. Thus we able to implement strictest now and after some time of new feature using, relax the behaviour. This will not require significant efforts.
Re: DIP66 - Multiple alias this
On Friday, 10 October 2014 at 21:25:17 UTC, Walter Bright wrote: On 10/10/2014 10:09 AM, IgorStepanov wrote: Please, comment it. static assert(is(C : int)); //Ok, because C is subtype of int anyway. Comment should say that C is implicitly convertible to int Not really. int i = C(); //conflict: c.a.i vs c.b.i (thus, C is not implicitly convertible to int) struct Test1 { int a; int b; alias a this; alias b this; //Error: alias b this conflicts with alias a this; } DIP says this is obviously incorrect, but what rule is being applied? I suspect the rule here is if typeof(a)==typeof(b), then it is rejected. What if typeof(a) is implicitly convertible to typeof(b), and vice-versa? In current state, if typeof(a) is implicitly convertible to typeof(b), and vice-versa, compiler will accept this alias this declaration. However error may be raised at alias this resolving stage. Is alias this resolved before base class search, after base class search, or is it an error if both searches are successful? In existing alias this implementation alias this resolved after base class search. This DIP inherits it, thus now I suggest to check base classes before alias this. Is it acceptable? Should I add this into DIP? If 'a' and 'b' both contain overloads for function foo, then it should behave like imports do (which is a bit complex). Hmm. Now it works as I wrote it DIP pseudo-code: struct A { void foo(string); void foo(int); } struct B { void foo(double); } struct C { A a; B b; alias a this; alias b this; } C.foo(test); //found only one acceptable foo: C.a.foo(string) C.foo(5); //1. Check a: found C.a.foo(int); //2. Check b: found C.b.foo(double); //3. Raise error: C.a.foo(int) vs C.b.foo(double) conflict C.foo(5.0);//found only one acceptable foo: C.b.foo(double) Is it Ok? Essentially, the rules for multiple alias this should be the same as for multiple imports and multiple mixin templates. These rules work, and the consistency will be expected. Where can I read about multiple mixin templates?
Re: DIP66 - Multiple alias this
On Friday, 10 October 2014 at 21:26:49 UTC, Steven Schveighoffer wrote: On 10/10/14 5:15 PM, IgorStepanov wrote: On Friday, 10 October 2014 at 20:47:45 UTC, Steven Schveighoffer wrote: On 10/10/14 1:09 PM, IgorStepanov wrote: I've created DIP for my pull request. DIP: http://wiki.dlang.org/DIP66 PR: https://github.com/D-Programming-Language/dmd/pull/3998 Please, comment it. This part: void test() { C c; int i = c; //Error: c.a.i vs c.b.i } static assert(is(C : int)); //Ok, because C is subtype of int anyway. I think might be wrong. There is a lot of code out there that says, e.g.: void foo(T)(T t) if(is(T : U)) { U u = t; ... } Which will now create an error in the wrong place. IMO, the 'is' test should also fail. -Steve I thought exactly about this using case. See: You have a struct like this in first place: struct A { int i; alias i this; } struct C { A a; string s; alias a this; alias s this; } And you have a template function in second place: void foo(T)(T t) if(is(T : int)) { ... } void foo(T)(T t) if(is(T : string)) { ... } And you have the code it third place: C c; foo(c); //Error: what do you mean: foo!(T : string) or foo!(T : int) I agree with all this. Now, someone (A developer) changed the A definition: struct A { int i; alias i this; } struct B { int i; alias i this; } struct C { A a; B b; string s; alias a this; alias b this; alias s this; } And now, you code mystically start to works. Why? It's just as confused as before, no? The way the DIP reads, the call to foo(c) compiles, but the instantiation fails. This can cause subtle issues when you want to select an instantiation based on what it casts to. An example: foo(T)(T t) if(is(T : int)) { someFuncThatTakesInt(t); } foo(T)(T t) if(!is(T : int) is(T.shadow : int)) { someFuncThatTakesInt(t.shadow); } struct A { int i; alias i this; } struct B { int i; alias i this; } struct C { A a; B shadow; alias a this; alias shadow this; } C c; foo(c); // should compile, but I think your DIP makes it fail due to ambiguity -Steve You can write foo(c.shadow); This isn't hard. Ok, I understood you, let's listen to what others say
Re: DIP66 - Multiple alias this
On Friday, 10 October 2014 at 22:05:18 UTC, Timon Gehr wrote: On 10/10/2014 07:09 PM, IgorStepanov wrote: I've created DIP for my pull request. DIP: http://wiki.dlang.org/DIP66 PR: https://github.com/D-Programming-Language/dmd/pull/3998 Please, comment it. - C c; int i = c; //Error: c.a.i vs c.b.i static assert(is(C : int)); //Ok, because C is subtype of int anyway. So now we can have 'subtypes' whose instances cannot be stored in variables of the 'base type'? C++ allowed subtypes, which can not be casted to the base type (inheritance of two identical, non-virtual base classes). Ok, I've wrote my position, understood your and wait the decision of the arbitrator:) Such behaviour is inconsistent with both the reference implementation and the documentation (and the two happen to be mutually inconsistent on how 'is(:)' should behave as well. :o) ) - The following pseudo-code illustrates this: [...] Finally, if resultSet contains only one candidate, the compiler will accept it. This process might very well never terminate but it could terminate in more cases if it did something better than the naive brute-force search. I.e. either report cycles or don't keep exploring around cycles, but just looping indefinitely on cycles like the following is IMO not a good course of action: struct S{ alias get this; T get(){ return T.init; } } struct T{ alias get this; S get(){ return S.init; } int x; alias x this; } void main(){ S s; int x=s; } This case described in DIP below. Recursion tree will be like: s.get s.get.get -return, because T is already visited s.x - win Furthermore, the following code compiles now, but doesn't under the approach described in the DIP. Is this an actual regression your pull introduces or is there a bug in the pseudocode?: class A{ alias x this; int x; } class B: A{ alias y this; int y; } void main(){ int x = new B(); } The same issue also needs to be considered if A and B are structs instead and B has an additional alias this to an A (the solution might also be part of a fix for the cycle issue). - If resultSet contains more then one candidates, the compiler raises an error. struct A { short s; alias s this; } struct B { int i; alias i this; } struct C { A a; B b; alias a this; alias b this; } long l = C(); //What do you suggest?
Re: DIP66 - Multiple alias this
On Friday, 10 October 2014 at 22:18:36 UTC, Timon Gehr wrote: On 10/11/2014 12:07 AM, IgorStepanov wrote: On Friday, 10 October 2014 at 21:25:17 UTC, Walter Bright wrote: If 'a' and 'b' both contain overloads for function foo, then it should behave like imports do (which is a bit complex). Hmm. Now it works as I wrote it DIP pseudo-code: The pseudo-code doesn't actually specify that the arguments the identifier is being called with are considered at all. struct A { void foo(string); void foo(int); } struct B { void foo(double); } struct C { A a; B b; alias a this; alias b this; } C.foo(test); //found only one acceptable foo: C.a.foo(string) C.foo(5); //1. Check a: found C.a.foo(int); //2. Check b: found C.b.foo(double); //3. Raise error: C.a.foo(int) vs C.b.foo(double) conflict C.foo(5.0);//found only one acceptable foo: C.b.foo(double) Is it Ok? ... That is the right behaviour. What happens in this case: struct A{ void foo(int); void foo(double); } struct B void foo(string); } ... // (struct C and calls as yours) C.foo(test); //Ok, C.b.foo(string); C.foo(5); //Ok, C.a.foo(int); C.foo(5.0);//Ok, C.a.foo(double); Compiler simply tries to forward c.foo(ARG) - c.a.foo(ARG) and c.b.foo(ARG). If only one is correct, compiler will accept it. If both is correct, compiler will raise an error.
Re: DIP66 - Multiple alias this
On Friday, 10 October 2014 at 22:50:25 UTC, Walter Bright wrote: On 10/10/2014 3:20 PM, IgorStepanov wrote: The same issue also needs to be considered if A and B are structs instead and B has an additional alias this to an A (the solution might also be part of a fix for the cycle issue). - If resultSet contains more then one candidates, the compiler raises an error. struct A { short s; alias s this; } struct B { int i; alias i this; } struct C { A a; B b; alias a this; alias b this; } long l = C(); //What do you suggest? The rule would be if: long l = C.a(); long l = C.b(); both compile, then: long l = C(); must be an error, even if one of C.a() or C.b() might be a better match. This is how things work for template mixins and imports. So it is.
Re: DIP66 - Multiple alias this
BTW. Overloaded functions in PR resolves un-properly (raises error even if can be resolved correct function) However it's easy to fix and I'll do it tomorrow.
Re: DIP66 - Multiple alias this
On Friday, 10 October 2014 at 22:51:34 UTC, Walter Bright wrote: On 10/10/2014 3:46 PM, Timon Gehr wrote: On 10/11/2014 12:29 AM, Walter Bright wrote: On 10/10/2014 3:06 PM, Timon Gehr wrote: On 10/10/2014 11:25 PM, Walter Bright wrote: Essentially, the rules for multiple alias this should be the same as for multiple imports and multiple mixin templates. These rules work, and the consistency will be expected. Agreed. Do you suggest to overload alias this against imports and mixin templates? I hadn't thought of that (thanks for bringing it up). My first thought is no. Alias this gets searched after those do, because it comes into play only when the symbol isn't resolved in the scope. This allows for symbol hijacking (this is also the current behaviour): // --- module m; import std.stdio; // void foo(int x){ writeln(hi from m); } // uncomment to hijack // --- module main; import std.stdio; struct T{ import m; alias s this; S s; } struct S{ void foo(int x){ writeln(hi from S); } } void main(){ T t; t.foo(1); } Hmm. Good point. The alias this should be done before imports. Symmetrically. You may use symbol from import, uncomment it in aliased type and hijack it.
Re: Any libunwind experts n da house?
On Saturday, 27 September 2014 at 23:31:17 UTC, Andrei Alexandrescu wrote: On 9/27/14, 1:31 PM, IgorStepanov wrote: No, that for throwing from C++ into D: for catch an exception, we should pass type_info object to special C++ runtime function. C++ runtime determines, can throwed object type can be casted to asked type, and if yes - allow catch it and do catcher code. If you will see the my example, you will see that I do this manually: get throwed type_info and compare its mangle with requested mangle. If we will make it as possible, it will be work better, faster and reliable. As bonus: possibility to implement dynamic_cast over C++ classes. If that's what's needed, definitely please do explore it! But I defer expertise to Walter. -- Andrei Ok. Anyway, I can't work on this to the full extent, because I have a three D works (six pull requests), which are waiting for action from the D collaborators (UDA for modules PR reviewed and is waiting for approval, multiple alias this and new AA implementation are waiting for review). However, I've seen this direction and I want to report a few points: 1. C++ type_info/TypeHandle for classes is located on -1 index of vtbl; 2. type_info layout aren't standartized (as expected) and differs in G++, VS and (probably) DMC and SunC. 3. type_info in C++ uses in many different cases, like dynamic_cast and excetion handling. 4. D doensn't generate type_info and it can cause danger situation. e.g. //C++ code class CppBase { public: virtual void test() = 0; }; class CppDerived : public CppBase { public: void test(); }; void CppDerived::test() { std::cout CppDerived::test() std::endl; } void doTest(CppBase *obj) { obj-test(); CppDerived *dobj = dynamic_castCppDerived *(obj); //Attention! if (dobj) { std::cout casted std::endl; } else { std::cout fail std::endl; } } //D code extern(C++) interface CppBase { void test(); } class DDerived : CppBase { extern(C++) override void test() { writeln(DDerived.test()); } } extern(C++) void doTest(CppBase); void main() { writeln(start test); doTest(new DDerived()); //BOOM! segfault while processing dynamic_cast, because DDerived type_info is wrong. writeln(finish test); } //Now my suggestions: 1. We can implement type_info generation as library template like GenTypeInfo(T). It will return valid type_info object for supproted platforms and null for platforms, which isn't supported yet. 2. Compiler will use this template to get type_info and push it into vtbl (at -1 position) 3. In situation, when compile need to use type_info (may be try-catch with C++ exceptions or dynamic_cast, it will be raise error if type_info isn't implemented) This approach allows to move complex, platform-depended code from compiler to library. Also it allows to don't implement some platforms without user restrictions. In conclusion: this is a g++ type_info definitions: https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/cxxabi.h#L535 This has a flag __diamond_shaped_mask in __vmi_class_type_info, and __virtual_mask in __base_class_type_info. D allows multiply inheritance for interfaces. In mapping to C++: Is this inheritance virtual? Should we set __diamond_shaped_mask is A: B, C; B : D; C : D (B, C, D is interfaces)?
Re: Any libunwind experts n da house?
On Saturday, 27 September 2014 at 09:53:37 UTC, Jacob Carlborg wrote: On 2014-09-23 19:37, Andrei Alexandrescu wrote: We need a libunwind expert to figure out a good approach for handling exceptions thrown by C++ code into D. BTW, are you only interested in handling C++ exception, or are you interested in handling D exceptions in C++ as well? Ideally D should just use the same exception mechanism as C++. I don't think a language change is necessary for this. Changes in the compiler, yes, but hopefully not in the language. C++ exception mechanism uses C++ type_info objects. We can inherit object.Throwable from std::exception (through extern(C++) interface), override the what() method, but there are no way to generate C++ type_info for D class now. If we want to do a compiler support of C++ exceptions, we should implement and support another one non-standartized feature: type_info. BTW it allows to do dynamic_cast over C++ classes in D, but I think, nobody approve this suggestion, because it can be hard).
Re: Any libunwind experts n da house?
On Saturday, 27 September 2014 at 18:33:24 UTC, Jacob Carlborg wrote: On Saturday, 27 September 2014 at 11:34:32 UTC, IgorStepanov wrote: C++ exception mechanism uses C++ type_info objects. We can inherit object.Throwable from std::exception (through extern(C++) interface), override the what() method, but there are no way to generate C++ type_info for D class now. If we want to do a compiler support of C++ exceptions, we should implement and support another one non-standartized feature: type_info. BTW it allows to do dynamic_cast over C++ classes in D, but I think, nobody approve this suggestion, because it can be hard). Objective-C can do it somehow. If they can do it I'm sure we can as well. -- /Jacob Carlborg If someone from D commanders bless me, I can start to exploring and implementing std::type_info for D classes. If we made it, we will implement 50% of C++ exception handling.
Re: Any libunwind experts n da house?
On Saturday, 27 September 2014 at 19:16:24 UTC, Andrei Alexandrescu wrote: On 9/27/14, 2:53 AM, Jacob Carlborg wrote: On 2014-09-23 19:37, Andrei Alexandrescu wrote: We need a libunwind expert to figure out a good approach for handling exceptions thrown by C++ code into D. BTW, are you only interested in handling C++ exception, or are you interested in handling D exceptions in C++ as well? The more the merrier, but there's a large difference in importance. For the most part D code (new) will call into C++ code (old) so it's the C++ exceptions we're most worried about. -- Andrei Can someone implement an C++ exception transparency? (when exception, throwed from C++ can be passed through D function and landed down in C++ code). Then, if type_info is implemented, I will able to try to implement C++ exception catching.
Re: Any libunwind experts n da house?
On Saturday, 27 September 2014 at 20:18:42 UTC, Andrei Alexandrescu wrote: On 9/27/14, 12:53 PM, IgorStepanov wrote: On Saturday, 27 September 2014 at 18:33:24 UTC, Jacob Carlborg wrote: On Saturday, 27 September 2014 at 11:34:32 UTC, IgorStepanov wrote: C++ exception mechanism uses C++ type_info objects. We can inherit object.Throwable from std::exception (through extern(C++) interface), override the what() method, but there are no way to generate C++ type_info for D class now. If we want to do a compiler support of C++ exceptions, we should implement and support another one non-standartized feature: type_info. BTW it allows to do dynamic_cast over C++ classes in D, but I think, nobody approve this suggestion, because it can be hard). Objective-C can do it somehow. If they can do it I'm sure we can as well. -- /Jacob Carlborg If someone from D commanders bless me, I can start to exploring and implementing std::type_info for D classes. If we made it, we will implement 50% of C++ exception handling. Would that be for throwing exceptions from D into C++? I think we can postpone that for now. -- Andrei No, that for throwing from C++ into D: for catch an exception, we should pass type_info object to special C++ runtime function. C++ runtime determines, can throwed object type can be casted to asked type, and if yes - allow catch it and do catcher code. If you will see the my example, you will see that I do this manually: get throwed type_info and compare its mangle with requested mangle. If we will make it as possible, it will be work better, faster and reliable. As bonus: possibility to implement dynamic_cast over C++ classes.
Re: Any libunwind experts n da house?
On Saturday, 27 September 2014 at 20:11:34 UTC, Iain Buclaw via Digitalmars-d wrote: On 27 September 2014 10:53, Jacob Carlborg via Digitalmars-d digitalmars-d@puremagic.com wrote: On 2014-09-23 19:37, Andrei Alexandrescu wrote: We need a libunwind expert to figure out a good approach for handling exceptions thrown by C++ code into D. BTW, are you only interested in handling C++ exception, or are you interested in handling D exceptions in C++ as well? One ugly hack is to register a terminate handler. Then in the handler extract the necessary information from the exception, create a D exception with this information and throw it as a regular D exception. Throwing a D exception that should be catchable in C++ is a bit more tricky. It's possible to wrap the a call to a D function in a try-catch block. Convert the D exception to a C++ exception, then throw it using a function part of the C++ exception runtime. The problem here is that C++ won't be able to catch this exception because there's no personality function (or similar) setup by the D compiler. Ideally D should just use the same exception mechanism as C++. I don't think a language change is necessary for this. Changes in the compiler, yes, but hopefully not in the language. Well, ObjC++ shares the same EH personality routines as C++, which probably accounts for the compatibility. :) Iain. Is this way acceptable for D, or not. Why?
Re: Any libunwind experts n da house?
On Thursday, 25 September 2014 at 13:58:23 UTC, Andrei Alexandrescu wrote: On 9/25/14, 4:55 AM, Kagamin wrote: On Wednesday, 24 September 2014 at 15:07:05 UTC, Andrei Alexandrescu wrote: I wonder how difficult would be to create a wrapper class CppException for everything derived from C++ std::exception. Why not catch std::exception directly? Then you could generate code, just like C++ compiler does it. That would be a language change - right now D can only catch Exception objects. Andrei I've implemented an experemental framework to catching C++ exceptions from D. https://github.com/IgorStepanov/D-CPP-Exception-Handle It works with G++, and depends on unwind-cxx.h header, which has been stealed from libstdc++/libsupc++/. libsupc++ is a part of libstdc++ and this file can be found in libstdc++ sources. However, libstdc++ developers doesn't publish this header and it cannot be found in /usr/include/ This framework allows to call external C++ function and call exception, if good handler is passed. Also it can process polymorphic exceptions: in attached example I throws std::logic_exception and catches std::exception: //C++ #include stdexcept //test function void throwEx(void *) { throw std::logic_error(Catch me, if you can); } //D extern(C++) void throwEx(void *); void main() { /* code like ... try { throwEx(null); } catch(std.exception val) { printf(exception: '%s'\n, val.what()); } may be rewritten as */ Try!(throwEx)( (CPPException!int ex) { printf(exception: '%d'\n, *ex.data); return 0; }, (CPPException!(stdexceptions.std.exception) ex) { printf(exception: '%s'\n, ex.data.what()); return 0; } ); } //prints: exception: 'Catch me, if you can' However, DMD can't pass С++ exceptions through D function (including extern(C++)). Thus, we cant pass delegate to Try function instead of throwEx, but this trouble can be resolved, I think.
Re: Any libunwind experts n da house?
On Tuesday, 23 September 2014 at 17:37:42 UTC, Andrei Alexandrescu wrote: We need a libunwind expert to figure out a good approach for handling exceptions thrown by C++ code into D. Is anyone fluent with libunwind? Andrei Is there plans to catching C++ exceptions in D? What kind of C++ exceptions planned to catch in D? For example C++ can throw primitive types exceptions, for example int. Of cource, them can be wrapped into special D CPPException : Throwable, class, which contains string representation of C++ exception. What about C++ polymorthic exceptions? AFAIK, C++ exception are throwing by-value, and catching by ref (to allow polimorthic catch). Where C++ calls destructor for exception? In catch block? In D GC handles an exception lifetime. Is there any ideas about it? P.S. I tell about user-side of catching C++ exceptions, not about implementation.
Re: Any libunwind experts n da house?
On Wednesday, 24 September 2014 at 19:28:50 UTC, Andrei Alexandrescu wrote: On 9/24/14, 9:54 AM, Iain Buclaw via Digitalmars-d wrote: On 24 September 2014 16:07, Andrei Alexandrescu via Digitalmars-d digitalmars-d@puremagic.com wrote: I wonder how difficult would be to create a wrapper class CppException for everything derived from C++ std::exception. It would not save the exact exception, but it would save its what() string and perhaps the typeid().name(). A translator would create CppException objects from std::exception objects and their derivatives. How hard would that be? Please advise. Thinking about it: - Identifying a C++ exception, simple. Noice. - Identifying whether a D catch handler for a C++ exception object matches, tricky - maybe. ABI of structs being a potential maintenance burden - though you'd hope that they only change ABI once every two years or so. Second, determining that the C++ object being thrown and catch handler we are examining match might be awkward from D. That is something that needs investigation. Yah. I'm thinking of simplifying assumptions, e.g. all C++ exceptions map to one single D type called CppException, and we can assume there's always a D handler on top of the stack. All the CppException saves is a copy of the what() message from the C++ exception. However, for sure, the easiest thing that could be done *now* that only needs a slight EH library tweak is using catch-all handlers to recover from any language exception. try { SomeCxxFuncThatMayThrow(); } catch { // Recover, but without knowing what happened. } But I'd imagine you'd actually want information to come with your caught exception, though. :) Well even a catch like that would definitely be an improvement. Andrei I have one freaky example for you. This example can be non-standart and danger but it works: C++ side: http://pastebin.ru/PceYCOEq special function catch exception, pass it to specified handler and re-throw, if handler not found D side: http://pastebin.ru/7FNBzXHw D defines a special handler and pass it to catchException result: D code call C++ function void throwEx(void *) { throw 4; } and prints: dside.CPPException!int.CPPException@(0): 4 Doesn't fire to me, please :)
Re: Any libunwind experts n da house?
Now DMD doesn't support thorowing C++ exceptions through D function: extern(C++) void throwEx(); //compiled by G++ and throws exception extern(C++) void dFunc(void*) { throwEx(); } catchException(dFunc, ...); //terminate programm instead of catch it. Should be works in gdc
Re: Any libunwind experts n da house?
On Wednesday, 24 September 2014 at 22:14:5 UTC, Andrei Alexandrescu wrote: On 9/24/14, 2:53 PM, IgorStepanov wrote: I have one freaky example for you. This example can be non-standart and danger but it works: C++ side: http://pastebin.ru/PceYCOEq special function catch exception, pass it to specified handler and re-throw, if handler not found D side: http://pastebin.ru/7FNBzXHw D defines a special handler and pass it to catchException result: D code call C++ function void throwEx(void *) { throw 4; } and prints: dside.CPPException!int.CPPException@(0): 4 Doesn't fire to me, please :) Awesome! How do we make it standard and safe? -- Andrei C++ hasn't standart abi( If we talk about g++, this code has a one conspicuous trouble: I don't know, how to correctly get a pointer to exception. Class exception_ptr has getter for it, but it is private. I'll think about another variants. And I don't know about cxxabi.h standartness. The second trouble: if we want to do something more exception printing, we should get an exception pointer to user. However, user shouldn't copy this pointer, because exception object will be destroyed in cpp side. Good news: this code (D side) can be generated by D compiler: try { // C++ exceptions danger } catch(CPPException!int e) { } catch(CPPException!myException e) { } Of course, only exact match of exception will be works. No polymorphism.
Last Postblit Optimization and r-value ref
I've created issue about last postblit call optimization (LPO): https://issues.dlang.org/show_bug.cgi?id=13492 As I wrote in issue description, this optimization can solve 90% r-value reference issue. Let's talk about remaining 10%. There are remain two issue: 1. value argument causes full copying it to stack (can be important for a large structs and static arrays) 2. When r-value passed to auto-ref function (or simply: to any function by value) then this value becomes a l-value and can be passed to the next call as l-value ref: void first(Foo f) { second(f); } void second(Foo f) { writeln(by value); globalFoo = f; //perform LPO } void second(ref Foo f) { writeln(by ref); globalFoo = f; //can't perform LPO } first(Foo(42)); //passed to first by value, to second by ref and prints by ref I suggest a small ABI change which can solve this issues without major language changes. 1. Let's pass all structs and static arrays by ref (Keeping a value semantic). 2. Let's forward value args (include passed by auto ref) to a value args of a next call See, how it will works with this changes and LPO: void second(ref Foo); [1] void second(Foo); [2] void first(Foo f) { second(f); second(f); } Translates to: void first(Foo f) { Foo __tmp1 = f; //postblit is called second(__tmp1); //[2] is called, f passed by ref second(f); //[2] is called, f passed by ref, postblit isn't called because LPO } If second() writes f to any storage (e. g. struct field), f will be saved to this storage without postblit and dtor calls and struct copying (for the second call of second()) This changes allow us simulate r-value refs without major language changes. Destroy!