digitalmars.D - DIP69 - Implement scope for escape proof references
- Walter Bright (9/9) Dec 04 2014 http://wiki.dlang.org/DIP69
- eles (3/8) Dec 04 2014 Making it implicit and requiring an explicit "escape" for
- Walter Bright (2/9) Dec 04 2014 Was afraid that would break too much code.
- eles (7/12) Dec 04 2014 An annotation for functions could make all variables "scope"?
- ketmar via Digitalmars-d (4/21) Dec 04 2014 please, don't steal this nice identifier! i love variables with name
- eles (3/13) Dec 04 2014 It was just an idea, I do not push for it.
- Shammah Chancellor (7/19) Dec 10 2014 No, this is super important. Break it all! This kind of change will
- ketmar via Digitalmars-d (5/7) Dec 11 2014 On Wed, 10 Dec 2014 23:06:13 -0800
- zeljkog (3/9) Dec 11 2014 Compiler switch: escape=I(gnore)|W(arning)|E(rror).
- ketmar via Digitalmars-d (10/23) Dec 04 2014 =20
- Walter Bright (2/5) Dec 04 2014 Yeah, still learning wiki markup!
- H. S. Teoh via Digitalmars-d (14/20) Dec 04 2014 I don't understand the line where rule 5 was invoked:
- H. S. Teoh via Digitalmars-d (12/33) Dec 04 2014 [...]
- Walter Bright (2/3) Dec 04 2014 done
- Robert burner Schadek (13/13) Dec 04 2014 thank you for pushing on this.
- John Colvin (4/16) Dec 04 2014 Haven't got the time to look at this in detail right now, but if
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (2/3) Dec 04 2014 Why? If I've explicitly designated a reference as scope, why
- Martin Nowak (8/11) Dec 04 2014 Agreed, it should also work for any other code with some function
- Walter Bright (3/6) Dec 04 2014 To interface to code that presents a safe interface, but does things und...
- Martin Nowak (19/20) Dec 04 2014 Great stuff.
- Walter Bright (16/36) Dec 04 2014 Yes, it would be written:
- Steven Schveighoffer (3/16) Dec 04 2014 Hm... did you mean `alias this borrow`?
- Walter Bright (2/3) Dec 04 2014 yes (hangs head in shame)
- bearophile (4/7) Dec 04 2014 No need to be ashamed for similar things, you are doing your best.
- H. S. Teoh via Digitalmars-d (11/21) Dec 04 2014 Hold on, how does this convert to T with a scoped result? Or did you
- Nick Treleaven (11/13) Dec 06 2014 While this DIP enabling the above to be memory-safe is awesome, a later
- Martin Nowak (3/9) Dec 04 2014 But when there is no scope on the argument, I could not call setVal with...
- Martin Nowak (2/13) Dec 04 2014 Ah, it's inferred. Makes sense now.
- Walter Bright (3/14) Dec 04 2014 Actually, you can. The difference between 'ref' and 'scope ref' paramete...
- bearophile (43/47) Dec 04 2014 This seems acceptable only if the compiler switch "-scope"
- bearophile (9/15) Dec 04 2014 Perhaps better:
- Walter Bright (11/46) Dec 04 2014 Safety by default is outside of the sco[pe (!) of this discussion. Also,...
- bearophile (69/82) Dec 04 2014 I agree that @system code should allow to circumvent the part of
- Walter Bright (8/25) Dec 04 2014 The attributes don't have much use for small programs. When a program is...
- bearophile (6/10) Dec 08 2014 If that topic is outside the scope of this discussion, then I
- Walter Bright (5/8) Dec 09 2014 1. You can always start a new thread here.
- ketmar via Digitalmars-d (15/21) Dec 04 2014 actually, for me explicit attributes doesn't work well at all. and D
- H. S. Teoh via Digitalmars-d (30/54) Dec 04 2014 In an ideal world, programmers would never have to know attributes even
- Daniel Murphy (5/19) Dec 04 2014 This can't be used to infer attributes that can produce errors - those
- Tobias Pankrath (11/44) Dec 05 2014 We could inter attributes if not specified instead of assuming a
- ketmar via Digitalmars-d (10/16) Dec 04 2014 and those interface files will ease creation of my pet project: dynamic
- ixid (2/8) Dec 05 2014 That is a rather unfriendly syntax, it is the kind that
- bearophile (5/13) Dec 05 2014 Can you show an example of the noisy code it causes?
- ixid (5/19) Dec 05 2014 [1,2].stack
- Nick Treleaven (16/24) Dec 07 2014 I think even if the compiler could infer them as static arrays, it may
- bearophile (4/6) Dec 07 2014 Or might not. The [$] proposal is very refined.
- Kagamin (5/6) Dec 04 2014 How inout fits the picture? It has some scope semantics too.
- Walter Bright (3/9) Dec 04 2014 Couldn't think of another way.
- Daniel N (18/23) Dec 04 2014 Looks pretty solid, but I have one question based on the
- Tobias Pankrath (3/13) Dec 04 2014 Attribute inference only works for function literals and template
- H. S. Teoh via Digitalmars-d (13/30) Dec 04 2014 However, AFAIK, template *classes* trigger attribute inference on its
- Walter Bright (3/12) Dec 04 2014 I agree, it's a good point. Scope inference cannot be done for virtual
- Manu via Digitalmars-d (3/18) Dec 05 2014 No comment...
- Daniel Murphy (2/12) Dec 04 2014 This looks really good. Nice work.
- Steven Schveighoffer (9/20) Dec 04 2014 "There can be at most one owner for any piece of data."
- Tobias Pankrath (19/35) Dec 04 2014 A variable's lifetime starts at the point of its declaration, and
- Steven Schveighoffer (7/10) Dec 04 2014 This is like saying you have multiple owners :) I don't see the reason
- Walter Bright (5/10) Dec 04 2014 The owner of the refcounted data is the refcounting wrapper - and the wr...
- deadalnix (5/10) Dec 04 2014 The RC mechanism is the owner. Ownership is loosly defined in
- Steven Schveighoffer (7/16) Dec 05 2014 Well, actually the DIP is pretty rigid, it speaks only of ownership in
- Steven Schveighoffer (12/23) Dec 04 2014 "Scope affects:
- Walter Bright (5/14) Dec 04 2014 If you think that is contradictory, you should have read the earlier dra...
- Steven Schveighoffer (24/24) Dec 04 2014 int* bar(scope int*);
- Walter Bright (14/36) Dec 04 2014 The scope return value does not affect what can be returned. It affects ...
- H. S. Teoh via Digitalmars-d (8/16) Dec 04 2014 [...]
- Steven Schveighoffer (11/49) Dec 04 2014 I assumed the scope return was so you could do things like:
- Walter Bright (7/34) Dec 05 2014 Why would a programmer do that? I often ask that question! But the langu...
- Steven Schveighoffer (10/41) Dec 08 2014 But you're the programmer that did it, it's YOUR example! :)
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (5/10) Dec 08 2014 It's for references to objects that are owned by the function (or
- Steven Schveighoffer (4/11) Dec 08 2014 Why not? An object is allocated on the heap, and has infinite lifetime.
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (12/30) Dec 08 2014 "object" as in "instance of struct" ;-)
- Steven Schveighoffer (22/43) Dec 08 2014 Why not? x has the same lifetime as container!
- H. S. Teoh via Digitalmars-d (67/79) Dec 04 2014 Finally! Thanks for the hard work, looking forward for this to be
- Walter Bright (18/80) Dec 04 2014 I think "type constructor" is the more pedantically correct term, but yo...
- H. S. Teoh via Digitalmars-d (50/136) Dec 04 2014 Recently there was a bug filed by Andrei himself, and a bunch of merged
- Walter Bright (22/102) Dec 04 2014 One is to interface with legacy code that does not use 'scope' properly ...
- H. S. Teoh via Digitalmars-d (19/89) Dec 04 2014 Not when all code is @system by default!
- Walter Bright (5/14) Dec 04 2014 I know:
- H. S. Teoh via Digitalmars-d (9/28) Dec 04 2014 I'm not saying it's necessary a bad thing... just that it will be an
- H. S. Teoh via Digitalmars-d (6/18) Dec 04 2014 [...]
- Walter Bright (2/3) Dec 04 2014 Ok, ok, I fixed the DIP :-)
- Walter Bright (15/27) Dec 04 2014 Turns out, 'ref' has exactly the same issue. The resolution is the same:
- Basile Burg (2/6) Dec 04 2014 Was the goal of the post to represent the new alias syntax ?
- Walter Bright (2/3) Dec 04 2014 ??
- deadalnix (17/29) Dec 04 2014 So as mentioned, there are various problem with this DIP :
- Walter Bright (11/24) Dec 04 2014 I don't believe this is correct. Rvalues can be assigned, just like:
- H. S. Teoh via Digitalmars-d (13/24) Dec 04 2014 [...]
- Walter Bright (2/10) Dec 04 2014 An 'int' is not a view!
- deadalnix (11/46) Dec 04 2014 Yes, this is the job of the optimizer to do this kind of stunt.
- Walter Bright (6/42) Dec 05 2014 I don't see any other way, nor do I see the practical problem.
- deadalnix (21/50) Dec 05 2014 The DIP say nothing about scoped rvalue having different behavior
- Walter Bright (4/56) Dec 06 2014 Yes, but I think the proposal allows for that.
- deadalnix (16/25) Dec 08 2014 I did:
- Steven Schveighoffer (12/23) Dec 05 2014 Can we take a step back here?
- Walter Bright (9/17) Dec 05 2014 The tl;dr version is when a declaration is tagged with 'scope', the cont...
-
Sebastiaan Koppe
(20/45)
Dec 05 2014
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (14/19) Dec 06 2014 Yes, this is much better. When I suggested it, it was rejected
- Sebastiaan Koppe (14/30) Dec 06 2014 Hmm, I see.
- eles (10/16) Dec 07 2014 I think D is in a good position to use the information available
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (24/32) Dec 07 2014 I don't think the breakage is a serious problem in this case, so
- deadalnix (5/25) Dec 08 2014 This is inherently about ownership. I have a proposal about this.
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (9/13) Dec 09 2014 Not really different. The activation record (stack frame) is
- deadalnix (7/21) Dec 09 2014 That why i say they are linked. I don't think your way of stating
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (13/19) Dec 10 2014 It does when you return parts of it, like if you pass in a binary
- eles (18/21) Dec 10 2014 Funny thing it is that it started exactly as a reaction to those
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (10/19) Dec 10 2014 I think he is making excuses for himself. Both Java and C++ are
- deadalnix (6/26) Dec 10 2014 That is completely off topic. this is a function parameter, you
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (6/9) Dec 10 2014 You have many scopes, if two different scopes pass in "horses" to
- deadalnix (7/16) Dec 10 2014 It is always safe to consider scopeness of the retrun value (if
- bearophile (5/7) Dec 10 2014 What syntax do you suggest to use if you want to extend it that
- deadalnix (5/12) Dec 10 2014 scope[symbol_name] would get the same lifetime as symbol_name for
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (15/20) Dec 11 2014 Linear typing is already extremely limiting, by limiting it even
- Nick Treleaven (10/12) Dec 11 2014 It will, use @system. Maybe this proposal will be supplemented later,
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (7/12) Dec 11 2014 Yes…
- bearophile (10/14) Dec 11 2014 Ideally a type system should be flexible enough, in practice
- deadalnix (4/18) Dec 11 2014 What is important is how much expressiveness you get from the
- deadalnix (4/25) Dec 11 2014 I have no idea what you are saying. It sounds like randomly
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (8/10) Dec 11 2014 What is your problem?
- Steven Schveighoffer (9/33) Dec 08 2014 This is not what I was asking for. What I wanted to know was, when I see...
- Walter Bright (3/7) Dec 14 2014 http://wiki.dlang.org/DIP69#Ref has examples of errors that are prevente...
- zeljkog (12/23) Dec 08 2014 If it is main goal, opposite looks more natural.
- Shammah Chancellor (10/42) Dec 10 2014 This is exactly why this feature should be default behavior with
- zeljkog (2/4) Dec 11 2014 It can be compiler switch escape=I(gnore)|W(arning)|E(rror).
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (88/88) Dec 05 2014 There are limitations this proposal has in comparison to my
- Walter Bright (26/107) Dec 05 2014 Good idea. Certainly, this is less powerful than your proposal. The ques...
- bearophile (6/10) Dec 05 2014 Another essential purpose of a similar proposal is to avoid some
- Manu via Digitalmars-d (72/186) Dec 06 2014 NO!!
- Jacob Carlborg (10/11) Dec 06 2014 I suggest you take the time and write down how your vision of "ref"
- Piotrek (12/22) Dec 06 2014 @Manu
- deadalnix (11/35) Dec 08 2014 I'd like to not polute this thread with the ref topic.
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (12/60) Dec 06 2014 That's very sad to hear. I think you have brought up some very
- bearophile (6/10) Dec 06 2014 (Perhaps this was already said) I think Ada used to have
- Walter Bright (26/74) Dec 06 2014 I know there's no easy way to derive a storage class from an expression....
- Manu via Digitalmars-d (165/245) Dec 06 2014 Perhaps it's the tasks I typically perform with meta?
- Walter Bright (42/201) Dec 06 2014 I don't know, but it's a mystery to me what you're doing that there's no...
- Daniel Murphy (19/36) Dec 06 2014 I expect a proposal in which 'always' means "inline or give an error" wo...
- deadalnix (3/13) Dec 08 2014 I don't think UDA is controversial, but the way it was done
- Jacob Carlborg (15/25) Dec 07 2014 I don't know about LDC but at least GDC allows you to use UDA's instead
- Iain Buclaw via Digitalmars-d (5/28) Dec 07 2014 of a pragma. Then you can create a dummy attribute for DMD (and LDC):
- Jacob Carlborg (4/6) Dec 07 2014 Good point.
- Andrei Alexandrescu (3/5) Dec 20 2014 I'm still 883 messages behind so here's a drive-by comment - it's time
- Andrei Alexandrescu (18/22) Dec 20 2014 Another drive-by comment: I understand the motivation for this and the
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (34/127) Dec 06 2014 ??? This is a problem on the semantic level, unrelated to
- Walter Bright (9/54) Dec 06 2014 Perhaps, but the original reason to even have 'ref' was so it could be a...
- Sebastiaan Koppe (10/42) Dec 07 2014 I understand that scope will not allow the contents of the
- Walter Bright (3/45) Dec 08 2014 It means the reference itself (the pointer) does not escape.
- deadalnix (3/5) Dec 08 2014 It can safely be returned if you consider its lifetime as the
- Walter Bright (3/8) Dec 09 2014 The only purpose to a 'scope ref' parameter is to say it isn't being ret...
- deadalnix (4/16) Dec 09 2014 That is a completely useless feature. Also, you want to have
- Walter Bright (2/15) Dec 10 2014 I disagree. It's critical for chaining one function to the next.
- deadalnix (3/4) Dec 10 2014 I one can't return, one can't chain.
- Walter Bright (2/5) Dec 10 2014 I guess I'm not seeing the problem.
- deadalnix (5/13) Dec 10 2014 a.foo().bar() is the same as bar(foo(a)), that is, using the
- Walter Bright (5/15) Dec 11 2014 Right, and you can chain when the declarations are:
- Nick Treleaven (10/19) Dec 07 2014 From the DIP:
- Daniel N (12/16) Dec 06 2014 One concern I had with your proposal was that it refers to a
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (5/22) Dec 06 2014 For D, this wouldn't be necessary, because parsing and semantic
- Dicebot (22/34) Dec 07 2014 Thanks a lot of trying to move forward with this. Also glad to
- Walter Bright (9/16) Dec 07 2014 scope ref int foo(ref int x);
- bearophile (9/12) Dec 07 2014 When C++ programmers say that D-style ranges can't do everything
- Walter Bright (4/10) Dec 07 2014 I agree, but it's hard to find that sweet spot. I think Java definitely ...
- Steven Schveighoffer (13/27) Dec 08 2014 So:
- Nick Treleaven (2/9) Dec 09 2014 Yes, because foo's argument is not scope, it can be returned.
- Steven Schveighoffer (9/22) Dec 09 2014 But I thought if you take a reference from the stack, it's inferred as
- Nick Treleaven (7/14) Dec 09 2014 I think (with this DIP) values on the stack can safely be passed as a
- Nick Treleaven (4/5) Dec 11 2014 A ref parameter essentially is a scope ref parameter, but it can also be...
- deadalnix (5/10) Dec 11 2014 Introducing a keyword to be able to add arbitrary restriction
- Dicebot (26/49) Dec 08 2014 This isn't the same as it does not propagate scope but just
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (16/70) Dec 08 2014 That's why I asked the question in
- Dicebot (5/23) Dec 08 2014 Yes I have seen that thread and have no idea what Walter has
- Walter Bright (15/29) Dec 08 2014 The difference between 'scope ref' and 'ref' parameters is that the form...
- Dicebot (26/47) Dec 08 2014 Easier to go straight with pseudo-code:
- Walter Bright (3/12) Dec 08 2014 Because it isn't just a non-null pointer (and ref's can still be null in...
- =?UTF-8?Q?Tobias=20M=C3=BCller?= (5/6) Dec 08 2014 AFAIK only if you dereference a NULL pointer, which is UB. So not really...
- Walter Bright (3/7) Dec 08 2014 Saying it's UB doesn't help in the slightest. You can still have null re...
- Dicebot (3/10) Dec 08 2014 Don't want to distract from topic that actually matters but would
- Walter Bright (2/22) Dec 08 2014 front() should return a 'scope ref string'.
- Dicebot (6/34) Dec 08 2014 Please check `foo()` once more - it needs to accept scope (ref)
- Walter Bright (3/37) Dec 09 2014 It can still be passed down as a 'ref' parameter.
- Dicebot (10/25) Dec 09 2014 But as far as I understand the spec it will result it this code
- Walter Bright (4/11) Dec 10 2014 If you want data to 'escape' from r.front, then it shouldn't be marked a...
- Dicebot (5/8) Dec 10 2014 Allowing or prohibiting escaping of r.front in that example
- Walter Bright (2/9) Dec 11 2014 One reason why with templates, they'll do scope inference.
- Dicebot (7/22) Dec 11 2014 So the answer really is "current proposal does not support it"?
- Walter Bright (2/18) Dec 11 2014 When you're relying on the definition of an argument, it is inherently a...
- deadalnix (11/15) Dec 08 2014 Please no.
- Dicebot (14/27) Dec 08 2014 My thoughts have went totally different direction:
- deadalnix (10/29) Dec 08 2014 Yes, that is my whole point whith the scope flag in expression.
- deadalnix (4/12) Dec 08 2014 Type qualifier for scope is the wrong tool.
- Dicebot (4/6) Dec 08 2014 I'd say it strange for directly the opposite reason - pure value
- Manu via Digitalmars-d (27/48) Dec 11 2014 Will it? It looks like foo can't be called with scope data?
- ixid (3/3) Dec 11 2014 Manu have you shown Walter some of the code where ref is
- Manu via Digitalmars-d (14/17) Dec 12 2014 I have discussed issues here in the past, but possibly not as
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (22/50) Dec 11 2014 This is a point that most people don't seem to understand yet,
- John Colvin (2/34) Dec 11 2014 OH! I had totally misunderstood that. Cheers for the explanation.
- Steven Schveighoffer (6/36) Dec 11 2014 Please, put this in the DIP! This is exactly the thing I had been asking...
- Walter Bright (3/6) Dec 11 2014 Marc is clearly better at explaining things than I am, as I've explained...
- deadalnix (7/46) Dec 11 2014 Wut ? You cante store anything with grear lifetime, including non
- Dicebot (7/13) Dec 11 2014 So far I tend to agree, unfortunately. Considering all provided
- Walter Bright (4/9) Dec 12 2014 The proposal provides escape proof passing of arguments. This is necessa...
- Jacob Carlborg (5/8) Dec 13 2014 Passing references to stack allocated data to functions safely is one
- Walter Bright (2/9) Dec 13 2014 'scope ref' does that.
- Dicebot (31/34) Dec 14 2014 I am looking for a tool to prevent escaping references to
- Walter Bright (8/30) Dec 14 2014 1. All inout actually does is reduce code copy/pasta, it is not critical...
- Dicebot (20/43) Dec 14 2014 Being forced to duplicate every single function in two flavors to
- Walter Bright (15/39) Dec 14 2014 It also works with all the lambdas, since source for them is always avai...
- Dicebot (33/85) Dec 15 2014 C++ const does not really restrict or affect anything "for real",
- Walter Bright (7/27) Dec 15 2014 C++ programmers constantly copy/pasta functions for the sole purpose of ...
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (16/29) Dec 15 2014 It's more than that. It has the potential (well, could have) to
- Walter Bright (5/7) Dec 15 2014 Sometimes, it isn't possible to see a cleaner way unless the dirty versi...
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (15/50) Dec 15 2014 Is it be possible to make `inout` a more general tool for type
- Dicebot (11/23) Dec 15 2014 Actually I think in neither case children should be declared as
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (10/35) Dec 15 2014 I see this as the same thing, conceptually (module the
- deadalnix (2/7) Dec 15 2014 Ha finally something start to make sense here.
- Walter Bright (3/9) Dec 15 2014 Well, the DIP does defined scope in terms of a 'view' in just this manne...
- deadalnix (3/18) Dec 16 2014 No, I actually think the concept of view in the proposal is a
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (33/48) Dec 16 2014 Don't you think there is a contradiction between this notion of
- Walter Bright (3/4) Dec 16 2014 I'm afraid I don't understand at all what you wrote.
- bearophile (5/6) Dec 16 2014 Perhaps reading about linear type systems could help:
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (5/9) Dec 17 2014 But note that we want a "relaxed" linear type system. In general,
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (7/18) Dec 17 2014 Pointer aliasing is an optimization killer, which is why C99
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (14/34) Dec 17 2014 I know, this is one of those applications. The one I was thinking
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (33/43) Dec 17 2014 Hmm, I kind of feel that D is having too many parameter
- deadalnix (6/26) Dec 17 2014 If you have owner ship, you have free. If you have a pair
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (10/14) Dec 17 2014 More powerful than what?
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (61/66) Dec 17 2014 Well, look at it this way: D1 took 1990s style C++ application
- Dicebot (10/25) Dec 19 2014 Such notion of "view" requires at least some elements of
- Walter Bright (5/12) Dec 20 2014 I have no idea how "some elements of transitivity" can even work. It's e...
- Dicebot (14/30) Dec 21 2014 .. and here I was about you to do exactly the same :P What in
- Walter Bright (3/7) Dec 21 2014 The point of transitive scoping would be if the root owned the data reac...
- Dicebot (9/24) Dec 22 2014 It doesn't matter of root owns the data. We _assume_ that as
- Walter Bright (6/8) Dec 22 2014 I do understand that. Making it work with the type system is another mat...
- Dicebot (49/59) Dec 29 2014 Ok, lets consider some examples of code I'd want to work with
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (20/51) Dec 12 2014 I'm not convinced either.
- Walter Bright (8/52) Dec 12 2014 It means you'll have to wrap the next level of indirection in a ref coun...
- Manu via Digitalmars-d (14/61) Dec 12 2014 Ummm. I reckon there's a good reason that people don't seem to
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (12/79) Dec 12 2014 Unfortunately I have to agree.
- Manu via Digitalmars-d (4/13) Dec 12 2014 Oh okay. Is that still on the table?
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (8/28) Dec 12 2014 Dunno, maybe? DIP69 doesn't look very well-received at the
- deadalnix (2/6) Dec 12 2014 That won't fly. scope turtle left, type qualifier turtle right.
- deadalnix (8/13) Dec 12 2014 It appears to me that scopeness is a completely different beast
- Walter Bright (7/31) Dec 11 2014 I simply do not understand why distinguishing beteen ref and not-ref is ...
- Dmitry Olshansky (15/25) Dec 11 2014 I would hazard a guess that const T& is something lots of C++ got
- John Colvin (6/35) Dec 11 2014 Because he requires control over function ABIs for both
- Manu via Digitalmars-d (3/46) Dec 12 2014 Thank you. I feel like I'm just repeating myself over and over again.
- Walter Bright (2/9) Dec 12 2014 Example, please.
- Dicebot (7/11) Dec 11 2014 I don't see how this is related. It would be perfectly ok to
- deadalnix (8/18) Dec 11 2014 That is my proposal. However, it is not as simply as you think it
- Dicebot (4/25) Dec 11 2014 I don't hope it will be simple. It is all about making scope as
- Walter Bright (4/11) Dec 11 2014 Are you suggesting two kinds of scope - transitive and non-transitive?
- Dicebot (5/15) Dec 11 2014 I don't see applicability of non-transitive scope because I don't
- Walter Bright (9/21) Dec 12 2014 Consider every pointer in a tree to be a ref counted pointer. There is n...
- deadalnix (5/10) Dec 12 2014 Non transitive scope can be added without any language extension.
- Walter Bright (4/9) Dec 12 2014 In order for it to work, the holes in the language that enabled escapes ...
- deadalnix (6/21) Dec 12 2014 The holes covered by a non transitive scope are already undefined
- Walter Bright (5/21) Dec 12 2014 The beauty of GC is that you don't have to do any of this. To have safe ...
- deadalnix (3/10) Dec 12 2014 The whole point of scope is to reduce that cost, both in term of
- Walter Bright (3/13) Dec 12 2014 Yes, I do understand that :-) but currently doing a safe refcounted type...
- deadalnix (5/8) Dec 12 2014 Actually, no it doesn't. It allow to ensure that this is safe,
- Walter Bright (2/10) Dec 12 2014 How not?
- deadalnix (7/8) Dec 12 2014 scope a = new Stuff();
- Walter Bright (2/9) Dec 12 2014 RC needs to take a type as a parameter, not a symbol.
- Walter Bright (2/9) Dec 12 2014 What's the type signature of RC ?
- deadalnix (3/16) Dec 12 2014 RCWrapper!T RC(T)(scope T t) or somethign along these lines.
- Walter Bright (3/18) Dec 12 2014 And how does RC assign it to the wrapper? (It will fail to compile, and ...
- Manu via Digitalmars-d (47/86) Dec 12 2014 I've said so many times, it's the single greatest regular frustration
- Walter Bright (19/58) Dec 12 2014 You've said this before, many times, but what is lacking is an explanati...
- Manu via Digitalmars-d (82/149) Dec 12 2014 I did just give some examples, I'll repeat; auto ref fails when the
- Walter Bright (32/93) Dec 12 2014 If there's source to the function, it'll often be inlined which will rem...
- bearophile (5/13) Dec 13 2014 Giving a reference counter to every pointer in a binary tree
- Walter Bright (2/13) Dec 13 2014 DMD, for example, uses ASTs where the nodes have multiple parents.
- deadalnix (3/20) Dec 15 2014 It is a DAG then.
- Jacob Carlborg (5/8) Dec 13 2014 I think he meant taking the address of a function with auto ref.
- Walter Bright (4/10) Dec 13 2014 Yes, and C++ code is either ref or not ref, and it is not a problem to i...
- Manu via Digitalmars-d (175/274) Dec 13 2014 Do you think I just interact with other languages for fun or something?
- Paolo Invernizzi (8/31) Dec 14 2014 Philippe Sigaud Excellent "D Templates: A Tutorial"...
- ketmar via Digitalmars-d (9/14) Dec 14 2014 and that still doesn't work. ;-)
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (25/62) Dec 14 2014 Exactly. It shouldn't matter which type this data has - both
- Walter Bright (52/202) Dec 14 2014 I'm not trying to insult you - but why need 'auto ref' for extern functi...
- John Colvin (5/13) Dec 15 2014 That's not what he's talking about. My understanding is that Manu
- Max Samukha (5/10) Dec 13 2014 That is a misconception spread by C++. Templates are pure
- Walter Bright (7/13) Dec 13 2014 It took me an embarrassingly long time to get past the C++ notion that t...
- Shammah Chancellor (5/20) Dec 10 2014 I like the basics of the proposal and I think it's the right direction.
- Shammah Chancellor (6/29) Dec 10 2014 Also, more feedback. I would suggest that scope variable are the
http://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape. This, among other things, makes a ref counting type practical. It also makes it more practical to use other storage allocation schemes than garbage collection. It does not make scope into a type constructor, nor a general type-annotation system. It does not provide an ownership system, though it would complement one.
Dec 04 2014
On Thursday, 4 December 2014 at 09:25:11 UTC, Walter Bright wrote:http://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape.Making it implicit and requiring an explicit "escape" for un-scoped variables?
Dec 04 2014
On 12/4/2014 1:51 AM, eles wrote:On Thursday, 4 December 2014 at 09:25:11 UTC, Walter Bright wrote:Was afraid that would break too much code.http://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape.Making it implicit and requiring an explicit "escape" for un-scoped variables?
Dec 04 2014
On Thursday, 4 December 2014 at 10:00:37 UTC, Walter Bright wrote:On 12/4/2014 1:51 AM, eles wrote:On Thursday, 4 December 2014 at 09:25:11 UTC, Walter Bright wrote:http://wiki.dlang.org/DIP69Was afraid that would break too much code.An annotation for functions could make all variables "scope"? Similar to: int foo(int x) scopedvars { //all declarations are implicit "scope"; use "escape" for the others }
Dec 04 2014
On Thu, 04 Dec 2014 10:04:07 +0000 eles via Digitalmars-d <digitalmars-d puremagic.com> wrote:On Thursday, 4 December 2014 at 10:00:37 UTC, Walter Bright wrote:please, don't steal this nice identifier! i love variables with name "escape"! stolen "body" still hurts me.On 12/4/2014 1:51 AM, eles wrote:=20On Thursday, 4 December 2014 at 09:25:11 UTC, Walter Bright=20 wrote:http://wiki.dlang.org/DIP69Was afraid that would break too much code.=20 An annotation for functions could make all variables "scope"? =20 Similar to: =20 int foo(int x) scopedvars { //all declarations are implicit "scope"; use "escape" for the=20 others =20 }
Dec 04 2014
On Thursday, 4 December 2014 at 10:11:25 UTC, ketmar via Digitalmars-d wrote:On Thu, 04 Dec 2014 10:04:07 +0000 eles via Digitalmars-d <digitalmars-d puremagic.com> wrote:On Thursday, 4 December 2014 at 10:00:37 UTC, Walter Bright wrote:On 12/4/2014 1:51 AM, eles wrote:On Thursday, 4 December 2014 at 09:25:11 UTC, Walter Bright wrote:please, don't steal this nice identifier! i love variables with name "escape"! stolen "body" still hurts me.It was just an idea, I do not push for it.
Dec 04 2014
On 2014-12-04 10:00:28 +0000, Walter Bright said:On 12/4/2014 1:51 AM, eles wrote:No, this is super important. Break it all! This kind of change will significantly increase the performance of D code by almost eliminating GC thrashing. Having to type scope EVERYWHERE will make it unused and therefore unusable in other people's libraries. -Shammah.On Thursday, 4 December 2014 at 09:25:11 UTC, Walter Bright wrote:Was afraid that would break too much code.http://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape.Making it implicit and requiring an explicit "escape" for un-scoped variables?
Dec 10 2014
On Wed, 10 Dec 2014 23:06:13 -0800 Shammah Chancellor via Digitalmars-d <digitalmars-d puremagic.com> wrote:alas, not in this life. each "break our code" just making resistance harder. ;-)Was afraid that would break too much code.No, this is super important. Break it all!
Dec 11 2014
On Thursday, 4 December 2014 at 10:00:37 UTC, Walter Bright wrote:On 12/4/2014 1:51 AM, eles wrote:Compiler switch: escape=I(gnore)|W(arning)|E(rror). For transition default Ignore.Making it implicit and requiring an explicit "escape" for un-scoped variables?Was afraid that would break too much code.
Dec 11 2014
On Thu, 04 Dec 2014 01:24:13 -0800 Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:http://wiki.dlang.org/DIP69 =20 Despite its length, this is a fairly simple proposal. It adds the missing==20semantics for the 'scope' storage class in order to make it possible to p=ass a=20reference to a function without it being possible for it to escape. =20 This, among other things, makes a ref counting type practical. It also ma=kes it=20more practical to use other storage allocation schemes than garbage colle=ction.=20 It does not make scope into a type constructor, nor a general type-annota=tion=20system. =20 It does not provide an ownership system, though it would complement one.cosmetic issue: some comments are referring to rules by number ("Error, rule 5"), yet the rules aren't explicitly numbered. not a big deal, but still somewhat hard to follow.
Dec 04 2014
On 12/4/2014 1:53 AM, ketmar via Digitalmars-d wrote:cosmetic issue: some comments are referring to rules by number ("Error, rule 5"), yet the rules aren't explicitly numbered. not a big deal, but still somewhat hard to follow.Yeah, still learning wiki markup!
Dec 04 2014
On Thu, Dec 04, 2014 at 01:57:43AM -0800, Walter Bright via Digitalmars-d wrote:On 12/4/2014 1:53 AM, ketmar via Digitalmars-d wrote:I don't understand the line where rule 5 was invoked: scope int* a; ... scope int** f = &a; // Error, rule 5 Why is it an error, since f has a shorter lifetime than a? And what has it gotta do with rule 5, which currently reads: A scope ref variable can be initialized with another scope ref variable - scope ref is idempotent. ? T -- Real men don't take backups. They put their source on a public FTP-server and let the world mirror it. -- Linus Torvaldscosmetic issue: some comments are referring to rules by number ("Error, rule 5"), yet the rules aren't explicitly numbered. not a big deal, but still somewhat hard to follow.Yeah, still learning wiki markup!
Dec 04 2014
On Thu, Dec 04, 2014 at 10:31:07AM -0800, H. S. Teoh via Digitalmars-d wrote:On Thu, Dec 04, 2014 at 01:57:43AM -0800, Walter Bright via Digitalmars-d wrote:[...] Ah, nevermind, it should be rule 4, not rule 5. Rule 4 states that the address of scope variables cannot be assigned to another scope variable. Please fix the comment. ;-) T -- I've been around long enough to have seen an endless parade of magic new techniques du jour, most of which purport to remove the necessity of thought about your programming problem. In the end they wind up contributing one or two pieces to the collective wisdom, and fade away in the rearview mirror. -- Walter BrightOn 12/4/2014 1:53 AM, ketmar via Digitalmars-d wrote:I don't understand the line where rule 5 was invoked: scope int* a; ... scope int** f = &a; // Error, rule 5 Why is it an error, since f has a shorter lifetime than a? And what has it gotta do with rule 5, which currently reads: A scope ref variable can be initialized with another scope ref variable - scope ref is idempotent. ?cosmetic issue: some comments are referring to rules by number ("Error, rule 5"), yet the rules aren't explicitly numbered. not a big deal, but still somewhat hard to follow.Yeah, still learning wiki markup!
Dec 04 2014
On 12/4/2014 10:34 AM, H. S. Teoh via Digitalmars-d wrote:Please fix the comment. ;-)done
Dec 04 2014
thank you for pushing on this. Lifetime last bullet point: ", but lower than any variables in higher scopes." isn't that redundant to the first bullet point? Or am I missing something? Scope affects variables according to these rules: Could you enumerate the list instead of bullet points, I can't count. Base operation: That one I don't get. I would assume this is legal as the function is not safe. e = &c; // Error, lifetime(e's view) is ∞ and is greater than lifetime(c)
Dec 04 2014
On Thursday, 4 December 2014 at 09:25:11 UTC, Walter Bright wrote:http://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape. This, among other things, makes a ref counting type practical. It also makes it more practical to use other storage allocation schemes than garbage collection. It does not make scope into a type constructor, nor a general type-annotation system. It does not provide an ownership system, though it would complement one.Haven't got the time to look at this in detail right now, but if it's as good as it looks on the surface, I'm very excited to use it. Thanks for all the hard work, all involved.
Dec 04 2014
Errors for scope violations are only reported in safe code.Why? If I've explicitly designated a reference as scope, why should it be ignored in un- safe code?
Dec 04 2014
On Thursday, 4 December 2014 at 11:21:27 UTC, Marc Schütz wrote:Agreed, it should also work for any other code with some function to cast away scope. ref T unscope(scope ref T t) system { auto p = &t; return *p; }Errors for scope violations are only reported in safe code.Why? If I've explicitly designated a reference as scope, why should it be ignored in un- safe code?
Dec 04 2014
On 12/4/2014 3:21 AM, "Marc Schütz" <schuetzm gmx.net>" wrote:To interface to code that presents a safe interface, but does things under the hood that can't be verified.Errors for scope violations are only reported in safe code.Why? If I've explicitly designated a reference as scope, why should it be ignored in un- safe code?
Dec 04 2014
On Thursday, 4 December 2014 at 09:25:11 UTC, Walter Bright wrote:http://wiki.dlang.org/DIP69Great stuff. Will this be possible, it's a fairly important use-case. scope ref T setVal(scope ref T t) { t.val = 12; return t; } Another question, how would a reference counted pointer take advantage of scope, i.e. avoid the increment/decrement when being passed to a function? One solution would be to add a function that returns a scoped reference to the underlying value. struct RefCounted(T) { scope ref T borrow() { return *p; } } Will it be possible to deduce, that the lifetime of that scoped value is tied to the smart pointer?
Dec 04 2014
On 12/4/2014 4:03 AM, Martin Nowak wrote:On Thursday, 4 December 2014 at 09:25:11 UTC, Walter Bright wrote:Yes, it would be written: scope ref T setVal(ref T t) { t.val = 12; return t; }http://wiki.dlang.org/DIP69Great stuff. Will this be possible, it's a fairly important use-case. scope ref T setVal(scope ref T t) { t.val = 12; return t; }Another question, how would a reference counted pointer take advantage of scope, i.e. avoid the increment/decrement when being passed to a function? One solution would be to add a function that returns a scoped reference to the underlying value. struct RefCounted(T) { scope ref T borrow() { return *p; } } Will it be possible to deduce, that the lifetime of that scoped value is tied to the smart pointer?struct RefCounted(T) { T t; scope ref T borrow() { return t; } alias this t; } This enables RefCounted!T to be implicitly converted to a T, but with a scoped result. This is a critical feature, one I spent a lot of time thinking about, and hope it's right :-)
Dec 04 2014
On 12/4/14 3:41 PM, Walter Bright wrote:On 12/4/2014 4:03 AM, Martin Nowak wrote:Hm... did you mean `alias this borrow`? -SteveWill it be possible to deduce, that the lifetime of that scoped value is tied to the smart pointer?struct RefCounted(T) { T t; scope ref T borrow() { return t; } alias this t; } This enables RefCounted!T to be implicitly converted to a T, but with a scoped result. This is a critical feature, one I spent a lot of time thinking about, and hope it's right :-)
Dec 04 2014
On 12/4/2014 1:24 PM, Steven Schveighoffer wrote:Hm... did you mean `alias this borrow`?yes (hangs head in shame)
Dec 04 2014
Walter Bright:On 12/4/2014 1:24 PM, Steven Schveighoffer wrote:No need to be ashamed for similar things, you are doing your best. Bye, bearophileHm... did you mean `alias this borrow`?yes (hangs head in shame)
Dec 04 2014
On Thu, Dec 04, 2014 at 12:41:58PM -0800, Walter Bright via Digitalmars-d wrote: [...]struct RefCounted(T) { T t; scope ref T borrow() { return t; } alias this t; } This enables RefCounted!T to be implicitly converted to a T, but with a scoped result. This is a critical feature, one I spent a lot of time thinking about, and hope it's right :-)Hold on, how does this convert to T with a scoped result? Or did you mean: ... property scope ref T borrow() { ... } alias borrow this; ? T -- Let's call it an accidental feature. -- Larry Wall
Dec 04 2014
On 04/12/2014 21:23, H. S. Teoh via Digitalmars-d wrote:property scope ref T borrow() { return t; } alias borrow this;While this DIP enabling the above to be memory-safe is awesome, a later tweak to AliasThis grammar to allow storage classes could make 'borrow' redundant. Plain alias now allows storage classes: AliasDeclaration: alias StorageClassesopt BasicType Declarator ; So: TweakedAliasThis: alias StorageClassesopt Identifier this ; Given ref and now scope will be storage classes, that would then allow just: alias scope ref t this;
Dec 06 2014
On 12/04/2014 09:41 PM, Walter Bright wrote:Yes, it would be written: scope ref T setVal(ref T t) { t.val = 12; return t; }But when there is no scope on the argument, I could not call setVal with a local T variable.
Dec 04 2014
On 12/04/2014 10:54 PM, Martin Nowak wrote:On 12/04/2014 09:41 PM, Walter Bright wrote:Ah, it's inferred. Makes sense now.Yes, it would be written: scope ref T setVal(ref T t) { t.val = 12; return t; }But when there is no scope on the argument, I could not call setVal with a local T variable.
Dec 04 2014
On 12/4/2014 1:54 PM, Martin Nowak wrote:On 12/04/2014 09:41 PM, Walter Bright wrote:Actually, you can. The difference between 'ref' and 'scope ref' parameters is the latter cannot be returned by ref.Yes, it would be written: scope ref T setVal(ref T t) { t.val = 12; return t; }But when there is no scope on the argument, I could not call setVal with a local T variable.
Dec 04 2014
I am not expert in such things, but here are few comments and questions.Errors for scope violations are only reported in safe code.This seems acceptable only if the compiler switch "-scope" implies functions to be safe by default and system on request, because currently lot of D programmers don't apply annotations like safe to their D code. Opt-in safety doesn't work well (and in D we still have the problems caused by null pointers and references). - - - - - - - - - - Also consider an annotation like "!scope" or " escape" for the opposite purpose. - - - - - - - - - -Delegates currently defensively allocate closures with the GC. Few actually escape, and with scope only those that actually escape need to have the closures allocated.<So there's no need for extra annotations to make delegates nogc in most cases? - - - - - - - - - - Regarding array literals, some people proposed a syntax for fixed-size arrays to avoid heap-allocations (the "s" after the array literal): void foo(int[2]) {} void bar(int[]) {} void main() nogc { foo([1, 2]s); bar([1, 2]s); } Is DIP69 able to infer those arrays can be both stack-allocated? Is the "s" annotations still useful? - - - - - - - - - - Regarding the benefits, is escape analysis going to be used to allocate _automatically_ some small dynamic arrays and classes/structs on the stack instead of heap? And is escape analysis going to automatically give hints to the GC to deallocate faster GC-allocated memory that doesn't escape? void foo() { // Both dynamic arrays don't escape foo // Automatically stack allocated. auto a = new int[3]; // Heap-allocated but deterministically // deleted at the end of foo scope. auto b = new int[10_000]; } - - - - - - - - - - Bye, bearophile
Dec 04 2014
void foo(int[2]) {} void bar(int[]) {} void main() nogc { foo([1, 2]s); bar([1, 2]s); }Perhaps better: void foo(int[2]) {} void bar(scope int[]) {} void main() nogc { foo([1, 2]s); bar([1, 2]s); } Bye, bearophile
Dec 04 2014
On 12/4/2014 4:55 AM, bearophile wrote:I am not expert in such things, but here are few comments and questions.Safety by default is outside of the sco[pe (!) of this discussion. Also, you can merely put: safe: at the beginning of a module and it is now all safe.Errors for scope violations are only reported in safe code.This seems acceptable only if the compiler switch "-scope" implies functions to be safe by default and system on request, because currently lot of D programmers don't apply annotations like safe to their D code. Opt-in safety doesn't work well (and in D we still have the problems caused by null pointers and references).Also consider an annotation like "!scope" or " escape" for the opposite purpose.That's part of a more general issue of negation of attributes.Marking delegates as nogc does not affect the closure, as the closure is allocated when the enclosing function is entered.Delegates currently defensively allocate closures with the GC. Few actually escape, and with scope only those that actually escape need to have the closures allocated.<So there's no need for extra annotations to make delegates nogc in most cases?Regarding array literals, some people proposed a syntax for fixed-size arrays to avoid heap-allocations (the "s" after the array literal): void foo(int[2]) {} void bar(int[]) {} void main() nogc { foo([1, 2]s); bar([1, 2]s); } Is DIP69 able to infer those arrays can be both stack-allocated? Is the "s" annotations still useful?It's an idea worth exploring.Regarding the benefits, is escape analysis going to be used to allocate _automatically_ some small dynamic arrays and classes/structs on the stack instead of heap?This is an optimization opportunity that scope enables.And is escape analysis going to automatically give hints to the GC to deallocate faster GC-allocated memory that doesn't escape? void foo() { // Both dynamic arrays don't escape foo // Automatically stack allocated. auto a = new int[3]; // Heap-allocated but deterministically // deleted at the end of foo scope. auto b = new int[10_000]; }Another optimization opportunity.
Dec 04 2014
Walter Bright:I agree that system code should allow to circumvent the part of the type system that enforces the scoping (this happens in Rust too) (but perhaps this has to happen only on scopes that gets inferred and not the ones specified explicitly). Perhaps you can't leave the " safe on default" issue out of the DIP69. Currently most D code I see around is not tagged with safe. If people don't bother applying that annotation, all the work you will do to implement DIP69 will be wasted or partially wasted. ------------------ From your answer to H. S. Teoh:This seems acceptable only if the compiler switch "-scope" implies functions to be safe by default and system on request, because currently lot of D programmers don't apply annotations like safe to their D code. Opt-in safety doesn't work well (and in D we still have the problems caused by null pointers and references).Safety by default is outside of the sco[pe (!) of this discussion. Also, you can merely put: safe: at the beginning of a module and it is now all safe.Are there bug reports on this?I and other people have opened several bug reports on safe, some of them are (but others are present, this is a subset): https://issues.dlang.org/show_bug.cgi?id=13615 https://issues.dlang.org/show_bug.cgi?id=13681 https://issues.dlang.org/show_bug.cgi?id=12948 https://issues.dlang.org/show_bug.cgi?id=13607 https://issues.dlang.org/show_bug.cgi?id=12845 https://issues.dlang.org/show_bug.cgi?id=6646 https://issues.dlang.org/show_bug.cgi?id=11176 https://issues.dlang.org/show_bug.cgi?id=6333 https://issues.dlang.org/show_bug.cgi?id=13054 https://issues.dlang.org/show_bug.cgi?id=13506 https://issues.dlang.org/show_bug.cgi?id=13188 Some examples of the problems (but there are also opposite problems, like in Issue 6646): void main() safe { import std.stdio, std.algorithm, std.bigint, std.typecons, std.array; [1, 2].sort!("a < b", SwapStrategy.stable); auto r = [1, 2].sort().release; writeln; BigInt a; a = a + a; alias Foo = Tuple!int; Foo[] data; data.remove!(x => x == Foo()); int[] b; auto c = b.capacity; b.schwartzSort!(x => x); const r2 = cartesianProduct([1], [1]).array; [Typedef!int(1)].array; } Output, dmd 2.067alpha: test.d(3,11): Error: safe function 'D main' cannot call system function 'std.algorithm.sort!("a < b", cast(SwapStrategy)2, int[]).sort' test.d(4,25): Error: safe function 'D main' cannot call system function 'std.range.SortedRange!(int[], "a < b").SortedRange.release' test.d(5,5): Error: safe function 'D main' cannot call system function 'std.stdio.writeln!().writeln' test.d(7,9): Error: safe function 'D main' cannot call system function 'std.bigint.BigInt.opBinary!("+", BigInt).opBinary' test.d(10,9): Error: safe function 'D main' cannot call system function 'test.main.remove!((x) => x == Foo(), cast(SwapStrategy)2, Tuple!int[]).remove' test.d(12,15): Error: safe function 'D main' cannot call system function 'object.capacity!int.capacity' test.d(13,6): Error: safe function 'D main' cannot call system function 'test.main.schwartzSort!((x) => x, "a < b", cast(SwapStrategy)0, int[]).schwartzSort' test.d(14,42): Error: safe function 'D main' cannot call system function 'std.array.array!(Result).array' test.d(15,21): Error: safe function 'D main' cannot call system function 'std.array.array!(Typedef!(int, 0, null)[]).array' Bye, bearophile
Dec 04 2014
On 12/4/2014 2:08 PM, bearophile wrote:Perhaps you can't leave the " safe on default" issue out of the DIP69. Currently most D code I see around is not tagged with safe. If people don't bother applying that annotation, all the work you will do to implement DIP69 will be wasted or partially wasted.The attributes don't have much use for small programs. When a program is small enough that one person can keep it all in their head, there is not much use for it. I'd like to keep D usable for quick small program writing. Attributes become more and more useful the larger the program gets, and the more people are working on it. In such cases, I don't think it's near as much of a burden to put in a few annotations, and the payoff is significantly larger.I and other people have opened several bug reports on safe, some of them are (but others are present, this is a subset): https://issues.dlang.org/show_bug.cgi?id=13615 https://issues.dlang.org/show_bug.cgi?id=13681 https://issues.dlang.org/show_bug.cgi?id=12948 https://issues.dlang.org/show_bug.cgi?id=13607 https://issues.dlang.org/show_bug.cgi?id=12845 https://issues.dlang.org/show_bug.cgi?id=6646 https://issues.dlang.org/show_bug.cgi?id=11176 https://issues.dlang.org/show_bug.cgi?id=6333 https://issues.dlang.org/show_bug.cgi?id=13054 https://issues.dlang.org/show_bug.cgi?id=13506 https://issues.dlang.org/show_bug.cgi?id=13188Thanks you.
Dec 04 2014
Walter Bright:Safety by default is outside of the sco[pe (!) of this discussion. Also, you can merely put: safe: at the beginning of a module and it is now all safe.If that topic is outside the scope of this discussion, then I have opened an ER on it: https://issues.dlang.org/show_bug.cgi?id=13838 Bye, bearophile
Dec 08 2014
On 12/8/2014 3:34 PM, bearophile wrote:If that topic is outside the scope of this discussion, then I have opened an ER on it: https://issues.dlang.org/show_bug.cgi?id=138381. You can always start a new thread here. 2. Thanks for filing the E.R. 3. When filing such, please check the [Hardware] settings. You have it set to [x86][Windows]. I reset them to [All][All].
Dec 09 2014
On Thu, 04 Dec 2014 12:55:34 +0000 bearophile via Digitalmars-d <digitalmars-d puremagic.com> wrote:This seems acceptable only if the compiler switch "-scope"=20 implies functions to be safe by default and system on request,=20 because currently lot of D programmers don't apply annotations=20 like safe to their D code. Opt-in safety doesn't work well (and=20 in D we still have the problems caused by null pointers and=20 references).actually, for me explicit attributes doesn't work well at all. and D compiler can't infer atributes for functions (ok, it can, but it can't apply that without changing function signature). that's why alot of my code compiles awfully slow with separate compilation: i'm used to write argument-less templates everywhere, so compiler can infer and apply attributes by itself. i even stopped commenting that practice, 'cause it means that much of the code will carry comments like this: // template to allow attribute inference void foo() (A a, int c) { ... } i think that something is very wrong with the function attributes. but i still can't think out how to improve that. but it still feels wrong. that's not about "D designers made a mistake", that's about "i want to invent a better thing!" ;-)
Dec 04 2014
On Fri, Dec 05, 2014 at 03:16:36AM +0200, ketmar via Digitalmars-d wrote:On Thu, 04 Dec 2014 12:55:34 +0000 bearophile via Digitalmars-d <digitalmars-d puremagic.com> wrote:I've been considering to start adopting that practice too.This seems acceptable only if the compiler switch "-scope" implies functions to be safe by default and system on request, because currently lot of D programmers don't apply annotations like safe to their D code. Opt-in safety doesn't work well (and in D we still have the problems caused by null pointers and references).actually, for me explicit attributes doesn't work well at all. and D compiler can't infer atributes for functions (ok, it can, but it can't apply that without changing function signature). that's why alot of my code compiles awfully slow with separate compilation: i'm used to write argument-less templates everywhere, so compiler can infer and apply attributes by itself. i even stopped commenting that practice, 'cause it means that much of the code will carry comments like this: // template to allow attribute inference void foo() (A a, int c) { ... }i think that something is very wrong with the function attributes. but i still can't think out how to improve that. but it still feels wrong. that's not about "D designers made a mistake", that's about "i want to invent a better thing!" ;-)In an ideal world, programmers would never have to know attributes even existed, the compiler would infer everything and optimize based on that. However, we're not quite there yet. :-) (And I don't know when we'll ever get there. Maybe D3. But we probably won't see that in the next 10 years... probably more.) I've often pondered about the possibility of a language where the compiler will analyze each module and infer any number of attributes and optimization opportunities for each symbol exported by that module, and this information will be saved in the object file (or some other kind of interfacing file). This includes any half-compiled template bodies and whatever else that can't be fully codegen'd until actual use. The attributes will include all sorts of stuff that programmers normally wouldn't want to deal with -- there could be 10+ or 50+ attributes representing various optimization / static checking opportunities. Then every time a module is imported by another module, the compiler never goes to the source code of the imported module anymore, but it will read the object (interface) file, which is fully attributed, and the saved attributes will be used internally for static checking, optimization, and inferring attributes for the current module. This way, the programmer not only gets the benefit of attributes without actually having to explicitly use them, but the language designers can also add many more attributes than you'd ever want to manually type out, or even remove old attributes that are no longer useful. The interface files will also serve as a convenient (and superior) substitute for .di files (uuugly) or .h files (even worse). T -- Right now I'm having amnesia and deja vu at the same time. I think I've forgotten this before.
Dec 04 2014
"H. S. Teoh via Digitalmars-d" wrote in message news:mailman.2709.1417745546.9932.digitalmars-d puremagic.com...I've often pondered about the possibility of a language where the compiler will analyze each module and infer any number of attributes and optimization opportunities for each symbol exported by that module, and this information will be saved in the object file (or some other kind of interfacing file). This includes any half-compiled template bodies and whatever else that can't be fully codegen'd until actual use. The attributes will include all sorts of stuff that programmers normally wouldn't want to deal with -- there could be 10+ or 50+ attributes representing various optimization / static checking opportunities. Then every time a module is imported by another module, the compiler never goes to the source code of the imported module anymore, but it will read the object (interface) file, which is fully attributed, and the saved attributes will be used internally for static checking, optimization, and inferring attributes for the current module.This can't be used to infer attributes that can produce errors - those attributes have to be user-visible or the errors don't make any sense. If it's purely for optimization, then that's basically what LTO does.
Dec 04 2014
On Friday, 5 December 2014 at 02:38:48 UTC, Daniel Murphy wrote:"H. S. Teoh via Digitalmars-d" wrote in message news:mailman.2709.1417745546.9932.digitalmars-d puremagic.com...We could inter attributes if not specified instead of assuming a default, for example nogc and a possible gc. --- int[] foo1(int[] bar) nogc // function is nogc, error if gc is used int[] foo2(int[] bar) gc // function is gc, functions that call foo2 cannot be nogc int[] foo3(int[] bar) // neither nogc nor gc, compiler infers attribute ---I've often pondered about the possibility of a language where the compiler will analyze each module and infer any number of attributes and optimization opportunities for each symbol exported by that module, and this information will be saved in the object file (or some other kind of interfacing file). This includes any half-compiled template bodies and whatever else that can't be fully codegen'd until actual use. The attributes will include all sorts of stuff that programmers normally wouldn't want to deal with -- there could be 10+ or 50+ attributes representing various optimization / static checking opportunities. Then every time a module is imported by another module, the compiler never goes to the source code of the imported module anymore, but it will read the object (interface) file, which is fully attributed, and the saved attributes will be used internally for static checking, optimization, and inferring attributes for the current module.This can't be used to infer attributes that can produce errors - those attributes have to be user-visible or the errors don't make any sense. If it's purely for optimization, then that's basically what LTO does.
Dec 05 2014
On Thu, 4 Dec 2014 18:10:18 -0800 "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> wrote:This way, the programmer not only gets the benefit of attributes without actually having to explicitly use them, but the language designers can also add many more attributes than you'd ever want to manually type out, or even remove old attributes that are no longer useful. The interface files will also serve as a convenient (and superior) substitute for .di files (uuugly) or .h files (even worse).and those interface files will ease creation of my pet project: dynamic component framework a-la BlackBox Component Builder. i'm already thinking about exporting semi-processed ASTs as ".mda" files, so i can write tools that bypasses parsing and checking phases. such files can be used as module interfaces, as syntax completion databases and so on. they can even be processed with some bytecode generator to improve CTFE. yet my project plan is to release pre-alpha version not sooner than in 2030, so i'm not really working on it with full speed. ;-)
Dec 04 2014
void foo(int[2]) {} void bar(int[]) {} void main() nogc { foo([1, 2]s); bar([1, 2]s); }That is a rather unfriendly syntax, it is the kind that degenerates into noise with other structures.
Dec 05 2014
ixid:Can you show an example of the noisy code it causes? And are you able to invent something succint that is better? Bye, bearophilevoid foo(int[2]) {} void bar(int[]) {} void main() nogc { foo([1, 2]s); bar([1, 2]s); }That is a rather unfriendly syntax, it is the kind that degenerates into noise with other structures.
Dec 05 2014
On Friday, 5 December 2014 at 14:10:44 UTC, bearophile wrote:ixid:[1,2].stack stack [1,2] stack [1,2] [1,2]stackCan you show an example of the noisy code it causes? And are you able to invent something succint that is better? Bye, bearophilevoid foo(int[2]) {} void bar(int[]) {} void main() nogc { foo([1, 2]s); bar([1, 2]s); }That is a rather unfriendly syntax, it is the kind that degenerates into noise with other structures.
Dec 05 2014
On 04/12/2014 12:55, bearophile wrote:Regarding array literals, some people proposed a syntax for fixed-size arrays to avoid heap-allocations (the "s" after the array literal): void foo(int[2]) {} void bar(scope int[]) {} void main() nogc { foo([1, 2]s); bar([1, 2]s); }I think even if the compiler could infer them as static arrays, it may still be useful to be explicit sometimes. We can already use a library template: template staticArray(items...) { import std.traits; alias T = CommonType!items[items.length]; enum T staticArray = [items]; } auto s = staticArray!(1, 2); static assert(is(typeof(s) == int[2])); bar(staticArray!(1, 2)); This might also make the proposed 'int[$] = [...];' syntax unnecessary. I think jmdavis once wrote something similar - although I've used enum here in case it helps avoid function template bloat.
Dec 07 2014
Nick Treleaven:This might also make the proposed 'int[$] = [...];' syntax unnecessary.Or might not. The [$] proposal is very refined. Bye, bearophile
Dec 07 2014
On Thursday, 4 December 2014 at 09:25:11 UTC, Walter Bright wrote:http://wiki.dlang.org/DIP69How inout fits the picture? It has some scope semantics too. Section about expressions suggests you will tack scoping carefully, but then why would you need return by ref tricks, which don't rely on scope tracking and hence look hackable?
Dec 04 2014
On 12/4/2014 4:56 AM, Kagamin wrote:On Thursday, 4 December 2014 at 09:25:11 UTC, Walter Bright wrote:s/inout/ref/http://wiki.dlang.org/DIP69How inout fits the picture? It has some scope semantics too.Section about expressions suggests you will tack scoping carefully, but then why would you need return by ref tricks, which don't rely on scope tracking and hence look hackable?Couldn't think of another way.
Dec 04 2014
On Thursday, 4 December 2014 at 09:25:11 UTC, Walter Bright wrote:http://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape.Looks pretty solid, but I have one question based on the following two statements: 1) "Scope is inferred for function parameters if not specified, under the same circumstances as pure, nothrow, nogc and safety are inferred." 2) "Scope is covariant, meaning it can be added to overriding functions." How would this be handled in the current proposal? class C { int bar(ref T); // <-- inferred to be scope } class D : C { override int bar(ref T); // <-- inferred to be NOT scope (but cannot remove scope when overriding) }
Dec 04 2014
How would this be handled in the current proposal? class C { int bar(ref T); // <-- inferred to be scope } class D : C { override int bar(ref T); // <-- inferred to be NOT scope (but cannot remove scope when overriding) }Attribute inference only works for function literals and template functions. So no inference is done for both methods. Template methods can not be overwritten.
Dec 04 2014
On Thu, Dec 04, 2014 at 01:49:06PM +0000, Tobias Pankrath via Digitalmars-d wrote:However, AFAIK, template *classes* trigger attribute inference on its (non-template) member functions, so this would be problematic: class Base(T) { T data; void method(ref T); // inferred to be scope } class Derived : Base!int { override void method(ref T); // oops, cannot override } T -- I think Debian's doing something wrong, `apt-get install pesticide', doesn't seem to remove the bugs on my system! -- Mike DresserHow would this be handled in the current proposal? class C { int bar(ref T); // <-- inferred to be scope } class D : C { override int bar(ref T); // <-- inferred to be NOT scope (but cannot remove scope when overriding) }Attribute inference only works for function literals and template functions. So no inference is done for both methods. Template methods can not be overwritten.
Dec 04 2014
On 12/4/2014 10:45 AM, H. S. Teoh via Digitalmars-d wrote:However, AFAIK, template *classes* trigger attribute inference on its (non-template) member functions, so this would be problematic: class Base(T) { T data; void method(ref T); // inferred to be scope } class Derived : Base!int { override void method(ref T); // oops, cannot override }I agree, it's a good point. Scope inference cannot be done for virtual functions. I amended the DIP.
Dec 04 2014
On 5 December 2014 at 07:14, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 12/4/2014 10:45 AM, H. S. Teoh via Digitalmars-d wrote:No comment...However, AFAIK, template *classes* trigger attribute inference on its (non-template) member functions, so this would be problematic: class Base(T) { T data; void method(ref T); // inferred to be scope } class Derived : Base!int { override void method(ref T); // oops, cannot override }I agree, it's a good point. Scope inference cannot be done for virtual functions. I amended the DIP.
Dec 05 2014
"Walter Bright" wrote in message news:m5p99m$luk$1 digitalmars.com...http://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape. This, among other things, makes a ref counting type practical. It also makes it more practical to use other storage allocation schemes than garbage collection. It does not make scope into a type constructor, nor a general type-annotation system. It does not provide an ownership system, though it would complement one.This looks really good. Nice work.
Dec 04 2014
On 12/4/14 4:24 AM, Walter Bright wrote:http://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape. This, among other things, makes a ref counting type practical. It also makes it more practical to use other storage allocation schemes than garbage collection. It does not make scope into a type constructor, nor a general type-annotation system. It does not provide an ownership system, though it would complement one."There can be at most one owner for any piece of data." This doesn't seem right. For GC data, the GC owns the data, that is true. But for Ref-counted data, there is more than one owner, and only when all the owners disown the data can it be destroyed. I think there is a disconnect here, you can't say *nobody* owns the data, and if you say one variable owns the data, which one is it? Continuing to read... -Steve
Dec 04 2014
"There can be at most one owner for any piece of data." This doesn't seem right. For GC data, the GC owns the data, that is true. But for Ref-counted data, there is more than one owner, and only when all the owners disown the data can it be destroyed. I think there is a disconnect here, you can't say *nobody* owns the data, and if you say one variable owns the data, which one is it? Continuing to read... -SteveIt is in line with this definitionA variable owns the data it contains if, when the lifetime of the variable is ended, the data can be destroyed.and the definiton of the lifetime of a variableThe lifetime of variables is based purely on their lexical scope and order of declaration. The following rules define a hierarchy of lifetimes:A variable's lifetime starts at the point of its declaration, and ends with the lexical scope it is defined in. An (rvalue) expression's lifetime is temporary; it lives till the end of the statement that it appears in. The lifetime of A is higher than that of B, if A appears in a higher scope than B, or if both appear in the same scope, but A comes lexically before B. This matches the order of destruction of local variables. The lifetime of a function parameter is higher than that of that function's local variables, but lower than any variables in higher scopes. Because the lifetimes of any two variables are different by this definition and the definition of ownership links to variable lifetime, only one variable can own the data. So if you have multiple ref-counted slices to an array, no one owns the array until the ref-count goes down to 1. Question remains, if this is a definition of ownership we want to employ.There can be at most one owner for any piece of data.
Dec 04 2014
On 12/4/14 10:06 AM, Tobias Pankrath wrote:So if you have multiple ref-counted slices to an array, no one owns the array until the ref-count goes down to 1. Question remains, if this is a definition of ownership we want to employ.This is like saying you have multiple owners :) I don't see the reason to avoid that. It's not like you can't define the owners when there are multiple references. Only one of them will end up destroying the variable, but it's not some other entity that will do it, you have a clear definition of who the owners are. -Steve
Dec 04 2014
On 12/4/2014 6:58 AM, Steven Schveighoffer wrote:This doesn't seem right. For GC data, the GC owns the data, that is true. But for Ref-counted data, there is more than one owner, and only when all the owners disown the data can it be destroyed. I think there is a disconnect here, you can't say *nobody* owns the data, and if you say one variable owns the data, which one is it?The owner of the refcounted data is the refcounting wrapper - and the wrapper decides when to destroy the payload. (For GC, the "wrapper" is the GC system, which decides to destroy the payload when there are no longer references to it. Just like a ref counting system.)
Dec 04 2014
On Thursday, 4 December 2014 at 14:58:47 UTC, Steven Schveighoffer wrote:"There can be at most one owner for any piece of data." This doesn't seem right. For GC data, the GC owns the data, that is true. But for Ref-counted data, there is more than one owner, and only when all the owners disown the data can it be destroyed.The RC mechanism is the owner. Ownership is loosly defined in this DIp so that it do not close any door for future language evolution.
Dec 04 2014
On 12/4/14 5:48 PM, deadalnix wrote:On Thursday, 4 December 2014 at 14:58:47 UTC, Steven Schveighoffer wrote:Well, actually the DIP is pretty rigid, it speaks only of ownership in terms of variables -- which variable owns a piece of data. It doesn't allow this kind of ownership via a concept or condition. I would change the DIP to reflect this clarification, if that is what is intended. -Steve"There can be at most one owner for any piece of data." This doesn't seem right. For GC data, the GC owns the data, that is true. But for Ref-counted data, there is more than one owner, and only when all the owners disown the data can it be destroyed.The RC mechanism is the owner. Ownership is loosly defined in this DIp so that it do not close any door for future language evolution.
Dec 05 2014
On 12/4/14 4:24 AM, Walter Bright wrote:http://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape. This, among other things, makes a ref counting type practical. It also makes it more practical to use other storage allocation schemes than garbage collection. It does not make scope into a type constructor, nor a general type-annotation system. It does not provide an ownership system, though it would complement one."Scope affects: local variables allocated on the stack" ... "scope int i; // scope is ignored because integers are not references and so are not views" I think I understand what you are trying to say -- the (big S) Scope of a local variable cannot escape, but it serves no purpose to declare a local int as (keyword) scope, since it's not going to be assigned any references. But it reads contradictory. -Steve
Dec 04 2014
On 12/4/2014 7:04 AM, Steven Schveighoffer wrote:"Scope affects: local variables allocated on the stack" ... "scope int i; // scope is ignored because integers are not references and so are not views" I think I understand what you are trying to say -- the (big S) Scope of a local variable cannot escape, but it serves no purpose to declare a local int as (keyword) scope, since it's not going to be assigned any references. But it reads contradictory.If you think that is contradictory, you should have read the earlier drafts :-) Basically, I had to get straight in my head the difference between the scope of the variable and the scope of its payload. The keyword 'scope' only affects the payload.
Dec 04 2014
int* bar(scope int*); scope int* foo(); bar(foo()); // Ok, lifetime(foo()) > lifetime(bar()) I'm trying to understand how foo can be implemented in any case. It has no scope ints to return, so where does it get the int from? I don't see where the proposal defines what exactly can be returned via scope. Another thing I saw early on: void abc() { scope int* a; int* b; scope ref int* c = a; // Error, rule 5 scope ref int* d = b; // Ok int* i = a; // Ok, scope is inferred for i global_ptr = d; // Error, lifetime(d) < lifetime(global_ptr) global_ptr = i; // Error, lifetime(i) < lifetime(global_ptr) int* j; // Ok, scope is inferred for i global_ptr = j; // Ok, j is not scope } Does this mean ref can now be applied to a variable? I'm not sure what the difference between scope ref and scope is. is d defined as a reference to b, or is d defined as a new variable that is initialized to what b points at (a la C++ &) ? -Steve
Dec 04 2014
On 12/4/2014 7:25 AM, Steven Schveighoffer wrote:int* bar(scope int*); scope int* foo(); bar(foo()); // Ok, lifetime(foo()) > lifetime(bar()) I'm trying to understand how foo can be implemented in any case. It has no scope ints to return, so where does it get the int from?Could be from a global variable. Or a new'd value.I don't see where the proposal defines what exactly can be returned via scope.The scope return value does not affect what can be returned. It affects how that return value can be used. I.e. the return value cannot be used in such a way that it escapes the lifetime of the expression.Another thing I saw early on: void abc() { scope int* a; int* b; scope ref int* c = a; // Error, rule 5 scope ref int* d = b; // Ok int* i = a; // Ok, scope is inferred for i global_ptr = d; // Error, lifetime(d) < lifetime(global_ptr) global_ptr = i; // Error, lifetime(i) < lifetime(global_ptr) int* j; // Ok, scope is inferred for i global_ptr = j; // Ok, j is not scope } Does this mean ref can now be applied to a variable?scope ref int p = x; is the same meaning as: foo(scope ref int p); foo(x); as far as what scope ref means. I found it convenient to use scope ref local variables to describe semantics. Whether we want to actually enable their usage hinges on if there's a point to it. Technically, it should work.I'm not sure what the difference between scope ref and scope is. is d defined as a reference to b, or is d defined as a new variable that is initialized to what b points at (a la C++ &) ?Scope refers to the payload. A scope ref is applying scope to the implied ref pointer. ref works like C++ &.
Dec 04 2014
On Thu, Dec 04, 2014 at 12:58:56PM -0800, Walter Bright via Digitalmars-d wrote:On 12/4/2014 7:25 AM, Steven Schveighoffer wrote:[...][...] Ahhh, this certainly clears things up a lot. You should add this explanation to the DIP. T -- Famous last words: I *think* this will work...I don't see where the proposal defines what exactly can be returned via scope.The scope return value does not affect what can be returned. It affects how that return value can be used. I.e. the return value cannot be used in such a way that it escapes the lifetime of the expression.
Dec 04 2014
On 12/4/14 3:58 PM, Walter Bright wrote:On 12/4/2014 7:25 AM, Steven Schveighoffer wrote:Well, OK, but why do that?int* bar(scope int*); scope int* foo(); bar(foo()); // Ok, lifetime(foo()) > lifetime(bar()) I'm trying to understand how foo can be implemented in any case. It has no scope ints to return, so where does it get the int from?Could be from a global variable. Or a new'd value.I assumed the scope return was so you could do things like: scope int *foo(scope int *x) { return x; } which would be fine, I assume, right?I don't see where the proposal defines what exactly can be returned via scope.The scope return value does not affect what can be returned. It affects how that return value can be used. I.e. the return value cannot be used in such a way that it escapes the lifetime of the expression.My question was about how this kind of allows declaring a ref variable in the middle of a function, which was never allowed before. -SteveAnother thing I saw early on: void abc() { scope int* a; int* b; scope ref int* c = a; // Error, rule 5 scope ref int* d = b; // Ok int* i = a; // Ok, scope is inferred for i global_ptr = d; // Error, lifetime(d) < lifetime(global_ptr) global_ptr = i; // Error, lifetime(i) < lifetime(global_ptr) int* j; // Ok, scope is inferred for i global_ptr = j; // Ok, j is not scope } Does this mean ref can now be applied to a variable?scope ref int p = x; is the same meaning as: foo(scope ref int p); foo(x); as far as what scope ref means. I found it convenient to use scope ref local variables to describe semantics. Whether we want to actually enable their usage hinges on if there's a point to it. Technically, it should work.
Dec 04 2014
On 12/4/2014 1:32 PM, Steven Schveighoffer wrote:On 12/4/14 3:58 PM, Walter Bright wrote:Why would a programmer do that? I often ask that question! But the language allows it, therefore we must support it.On 12/4/2014 7:25 AM, Steven Schveighoffer wrote:Well, OK, but why do that?int* bar(scope int*); scope int* foo(); bar(foo()); // Ok, lifetime(foo()) > lifetime(bar()) I'm trying to understand how foo can be implemented in any case. It has no scope ints to return, so where does it get the int from?Could be from a global variable. Or a new'd value.No. A scope parameter means the value does not escape the function. That means you can't return it.I assumed the scope return was so you could do things like: scope int *foo(scope int *x) { return x; } which would be fine, I assume, right?I don't see where the proposal defines what exactly can be returned via scope.The scope return value does not affect what can be returned. It affects how that return value can be used. I.e. the return value cannot be used in such a way that it escapes the lifetime of the expression.My question was about how this kind of allows declaring a ref variable in the middle of a function, which was never allowed before.There's no technical reason it is disallowed - it's just that I didn't see a point to it.
Dec 05 2014
On 12/5/14 6:09 PM, Walter Bright wrote:On 12/4/2014 1:32 PM, Steven Schveighoffer wrote:But you're the programmer that did it, it's YOUR example! :) However, it's not just that, this is the ONLY example given as to why we support scope returns. The language allows it, therefore we must support it? But it's not allowed now, right? Why add it?On 12/4/14 3:58 PM, Walter Bright wrote:Why would a programmer do that? I often ask that question! But the language allows it, therefore we must support it.On 12/4/2014 7:25 AM, Steven Schveighoffer wrote:Well, OK, but why do that?int* bar(scope int*); scope int* foo(); bar(foo()); // Ok, lifetime(foo()) > lifetime(bar()) I'm trying to understand how foo can be implemented in any case. It has no scope ints to return, so where does it get the int from?Could be from a global variable. Or a new'd value.I think you should eliminate scope returns then. They are not useful. I can't think of a single reason why a newly allocated via GC or global reference return should have to be restricted to exist only within the statement. Both have infinite lifetimes. -SteveNo. A scope parameter means the value does not escape the function. That means you can't return it.The scope return value does not affect what can be returned. It affects how that return value can be used. I.e. the return value cannot be used in such a way that it escapes the lifetime of the expression.I assumed the scope return was so you could do things like: scope int *foo(scope int *x) { return x; } which would be fine, I assume, right?
Dec 08 2014
On Monday, 8 December 2014 at 15:12:51 UTC, Steven Schveighoffer wrote:I think you should eliminate scope returns then. They are not useful. I can't think of a single reason why a newly allocated via GC or global reference return should have to be restricted to exist only within the statement. Both have infinite lifetimes.It's for references to objects that are owned by the function (or object of which the function is a method). These don't have infinite lifetimes.
Dec 08 2014
On 12/8/14 10:45 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>" wrote:On Monday, 8 December 2014 at 15:12:51 UTC, Steven Schveighoffer wrote:Why not? An object is allocated on the heap, and has infinite lifetime. -SteveI think you should eliminate scope returns then. They are not useful. I can't think of a single reason why a newly allocated via GC or global reference return should have to be restricted to exist only within the statement. Both have infinite lifetimes.It's for references to objects that are owned by the function (or object of which the function is a method). These don't have infinite lifetimes.
Dec 08 2014
On Monday, 8 December 2014 at 16:04:42 UTC, Steven Schveighoffer wrote:On 12/8/14 10:45 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>" wrote:"object" as in "instance of struct" ;-) And I was referring to objects owned by something with finite lifetime, e.g. a container. struct Array(T) { scope ref T opIndex(size_t); } Array!int container; ref x = container[42]; // no container[42]++; // yes writeln(container[42]); // yesOn Monday, 8 December 2014 at 15:12:51 UTC, Steven Schveighoffer wrote:Why not? An object is allocated on the heap, and has infinite lifetime.I think you should eliminate scope returns then. They are not useful. I can't think of a single reason why a newly allocated via GC or global reference return should have to be restricted to exist only within the statement. Both have infinite lifetimes.It's for references to objects that are owned by the function (or object of which the function is a method). These don't have infinite lifetimes.
Dec 08 2014
On 12/8/14 11:27 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>" wrote:On Monday, 8 December 2014 at 16:04:42 UTC, Steven Schveighoffer wrote:Why not? x has the same lifetime as container! This is an incorrect arbitrary decision: module foo; Array!int arr; int *x; void bar() { x = &arr[0]; // should be ok. } A struct's member function has no idea of its lifetime, except what it's told. Now, it would make more sense for this: ref T opIndex(size_t); scope ref T opIndex(size_t) scope; But it's sounding to me (from Walter's other comments) like the latter function can't exist, because you can't return scope from scope? And furthermore, there's no way to overload based on scope? I'm really not liking this whole proposal, it either needs to be explained better, or rewritten. -SteveOn 12/8/14 10:45 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>" wrote:"object" as in "instance of struct" ;-) And I was referring to objects owned by something with finite lifetime, e.g. a container. struct Array(T) { scope ref T opIndex(size_t); } Array!int container; ref x = container[42]; // noOn Monday, 8 December 2014 at 15:12:51 UTC, Steven Schveighoffer wrote:Why not? An object is allocated on the heap, and has infinite lifetime.I think you should eliminate scope returns then. They are not useful. I can't think of a single reason why a newly allocated via GC or global reference return should have to be restricted to exist only within the statement. Both have infinite lifetimes.It's for references to objects that are owned by the function (or object of which the function is a method). These don't have infinite lifetimes.
Dec 08 2014
On Thu, Dec 04, 2014 at 01:24:13AM -0800, Walter Bright via Digitalmars-d wrote:http://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape.Finally! Thanks for the hard work, looking forward for this to be implemented (in some shape of form -- see comments below).This, among other things, makes a ref counting type practical. It also makes it more practical to use other storage allocation schemes than garbage collection. It does not make scope into a type constructor, nor a general type-annotation system.[...] Can we pretty please use the term "type qualifier" instead of "type constructor"? ;-) Anyway, here are a few comments: 1) Why are scope violations only reported for safe code?? IMO this greatly limits the usefulness of this DIP. If I mark something as scope, I'd expect it should be enforced by the compiler regardless of safe annotations, otherwise what's the point?? Currently, due to the incompleteness of safe, it's difficult to use safe annotations everywhere I'd like to (e.g. I need to use some un- safe Phobos functions that really ought to be safe, but aren't due to various reasons, like compiler limitations, etc.). This greatly limits the usefulness of this DIP, if scope is only enforced in safe code! 2) Is there a way to detect if something is marked as scope? If not, how does this proposal actually enable ref-counted types? (I'm assuming a library ref-counting type here; or are we expecting further compiler enhancements for ref counting?) 3) What does scope mean for delegate parameters? To what does the scope apply, the delegate itself, its body, or its return value, or ...? struct S { void opApply(scope void delegate(ref int) loopBody) { ... // what restrictions (might) apply here w.r.t. // how loopBody can be called? } } And what would be the effect on the caller's side? S s; foreach (i; s) { // what restrictions (might) apply here? } 4) Under "Expressions", how does scope interact with overloaded operators? Do the same rules apply to expressions that use overloaded operators as they apply to built-in operators, or do they apply as though the overloaded operators were written out in function-call syntax? What happens if some overloaded operators take a mix of scope and non-scope parameters? Finally, the following isn't directly related to this DIP, since scope is intended to solve this problem, but I thought I should bring it up. :-) In the section "escaping via return", 5 points are listed as sufficient for detecting the "return func(t);" case. The following section "scope ref" states that these 5 points are "correct" (in implementing escaping reference detection). However, isn't the following a loophole? struct S { int x; } ref int func(ref S s) { return s.x; } ref T foo() { S s; return func(s); // escaping reference } Since S cannot implicitly convert to int, it would appear that this circumvents escaping reference detection. (In fact, dmd happily accepts this code, even if everything is annotated with safe.) Note that the escaping reference to x can be arbitrarily deeply nested inside S, so it's non-trivial to decide whether there's no possibility for references to (parts of) S to leak from func(). T -- If I were two-faced, would I be wearing this one? -- Abraham Lincoln
Dec 04 2014
On 12/4/2014 11:41 AM, H. S. Teoh via Digitalmars-d wrote:Can we pretty please use the term "type qualifier" instead of "type constructor"? ;-)I think "type constructor" is the more pedantically correct term, but you'll have to ask Andrei :-)1) Why are scope violations only reported for safe code?? IMO this greatly limits the usefulness of this DIP. If I mark something as scope, I'd expect it should be enforced by the compiler regardless of safe annotations, otherwise what's the point??Because there are inevitably cases where you'll need to wrap unsafe code with a safe interface. By screwing this down too tightly for system code, you'll force people to use really ugly workarounds.Currently, due to the incompleteness of safe, it's difficult to use safe annotations everywhere I'd like to (e.g. I need to use some un- safe Phobos functions that really ought to be safe, but aren't due to various reasons, like compiler limitations, etc.). This greatly limits the usefulness of this DIP, if scope is only enforced in safe code!Are there bug reports on this?2) Is there a way to detect if something is marked as scope?Using __traits(compiles,...) ought to work.If not, how does this proposal actually enable ref-counted types? (I'm assuming a library ref-counting type here; or are we expecting further compiler enhancements for ref counting?)The ref counting would be done by a wrapper, which implicitly converts to the wrappee by exposing a scope ref.3) What does scope mean for delegate parameters? To what does the scope apply, the delegate itself, its body, or its return value, or ...? struct S { void opApply(scope void delegate(ref int) loopBody) { ... // what restrictions (might) apply here w.r.t. // how loopBody can be called? } }Hmmm, looks like a problem I didn't think of. darnit!And what would be the effect on the caller's side? S s; foreach (i; s) { // what restrictions (might) apply here? }i would get its scope inferred as necessary, so restrictions would apply as they would to a 'scope i'.4) Under "Expressions", how does scope interact with overloaded operators? Do the same rules apply to expressions that use overloaded operators as they apply to built-in operators, or do they apply as though the overloaded operators were written out in function-call syntax? What happens if some overloaded operators take a mix of scope and non-scope parameters?Overloaded operators are treated like calls to the functions that back them.Finally, the following isn't directly related to this DIP, since scope is intended to solve this problem, but I thought I should bring it up. :-) In the section "escaping via return", 5 points are listed as sufficient for detecting the "return func(t);" case. The following section "scope ref" states that these 5 points are "correct" (in implementing escaping reference detection). However, isn't the following a loophole? struct S { int x; } ref int func(ref S s) { return s.x; } ref T foo() { S s; return func(s); // escaping reference } Since S cannot implicitly convert to int, it would appear that this circumvents escaping reference detection.No, because under this proposal, s.x is treated as if it were just s as far a scope rules are concerned.(In fact, dmd happily accepts this code, even if everything is annotated with safe.)I know, the current escape detection is quite inadequate - hence this proposal.Note that the escaping reference to x can be arbitrarily deeply nested inside S, so it's non-trivial to decide whether there's no possibility for references to (parts of) S to leak from func().This proposal should lock that down.
Dec 04 2014
On Thu, Dec 04, 2014 at 01:10:31PM -0800, Walter Bright via Digitalmars-d wrote:On 12/4/2014 11:41 AM, H. S. Teoh via Digitalmars-d wrote:Recently there was a bug filed by Andrei himself, and a bunch of merged PRs, that renamed "type constructor" to "type qualifier". The general sense I got was that we're deprecating "type constructor" in preference for "type qualifier".Can we pretty please use the term "type qualifier" instead of "type constructor"? ;-)I think "type constructor" is the more pedantically correct term, but you'll have to ask Andrei :-)Are there any use cases for bypassing scope semantics in system code? How common are they? Maybe such code *should* look ugly -- to draw attention to the fact that something un- safe is going on. Or perhaps we can make cast() strip away scope? I much rather require explicit annotation for potentially unsafe operations, than to have subtle bugs creep into the code just because I forgot to mark something as safe.1) Why are scope violations only reported for safe code?? IMO this greatly limits the usefulness of this DIP. If I mark something as scope, I'd expect it should be enforced by the compiler regardless of safe annotations, otherwise what's the point??Because there are inevitably cases where you'll need to wrap unsafe code with a safe interface. By screwing this down too tightly for system code, you'll force people to use really ugly workarounds.There are, though probably incomplete, and there's also an ongoing stream of Phobos PR's for marking things safe that ought not to be system. There's a *lot* of Phobos code that needs to be cleaned up in this way before this can be workable. (Unless we abuse trusted as a temporary workaround -- but that's not an approach I'd like to recommend!)Currently, due to the incompleteness of safe, it's difficult to use safe annotations everywhere I'd like to (e.g. I need to use some un- safe Phobos functions that really ought to be safe, but aren't due to various reasons, like compiler limitations, etc.). This greatly limits the usefulness of this DIP, if scope is only enforced in safe code!Are there bug reports on this?I see.2) Is there a way to detect if something is marked as scope?Using __traits(compiles,...) ought to work.Just saw your other post on this topic, makes sense. Still not sure how it would address the RC folks' request for compiler support, but I suppose that's a different topic.If not, how does this proposal actually enable ref-counted types? (I'm assuming a library ref-counting type here; or are we expecting further compiler enhancements for ref counting?)The ref counting would be done by a wrapper, which implicitly converts to the wrappee by exposing a scope ref.I think it's a fairly important issue, since making this work correctly will also open up optimization opportunities for a good chunk of delegate-based code.3) What does scope mean for delegate parameters? To what does the scope apply, the delegate itself, its body, or its return value, or ...? struct S { void opApply(scope void delegate(ref int) loopBody) { ... // what restrictions (might) apply here w.r.t. // how loopBody can be called? } }Hmmm, looks like a problem I didn't think of. darnit!I was thinking more of what implications may hold in the loop body for references to (local) variables outside the loop, since that presents another potential optimization opportunity if we do it right.And what would be the effect on the caller's side? S s; foreach (i; s) { // what restrictions (might) apply here? }i would get its scope inferred as necessary, so restrictions would apply as they would to a 'scope i'.OK. You might want to state this explicitly in the DIP, just so it isn't left up to interpretation. :-)4) Under "Expressions", how does scope interact with overloaded operators? Do the same rules apply to expressions that use overloaded operators as they apply to built-in operators, or do they apply as though the overloaded operators were written out in function-call syntax? What happens if some overloaded operators take a mix of scope and non-scope parameters?Overloaded operators are treated like calls to the functions that back them.[...] I'm not quite sure I see how this could be implemented. When the compiler is compiling func(), say it's in a different module, there's nothing about the function that says it's illegal to return a reference to the member of an incoming argument. First of all, this isn't a template function so scope inference isn't in effect; secondly, even of it were in effect (say we write it as `func()(ref S s)` instead), it wouldn't be able to infer the return value as scope, since it's obviously escaping a reference to a function argument. So, unless I'm missing something obvious, func() will be accepted by the compiler (as it should, since we didn't mark anything as scope here). Now when the compiler compiles foo(), it can no longer see what's inside func(), so there's no way for it to know that the ref int being returned actually comes from the ref S parameter. Furthermore, func()'s signature is not annotated with scope, so how would the compiler know where to apply scoping rules? Unless I'm missing something, this still looks like a loophole. T -- If you compete with slaves, you become a slave. -- Norbert WienerFinally, the following isn't directly related to this DIP, since scope is intended to solve this problem, but I thought I should bring it up. :-) In the section "escaping via return", 5 points are listed as sufficient for detecting the "return func(t);" case. The following section "scope ref" states that these 5 points are "correct" (in implementing escaping reference detection). However, isn't the following a loophole? struct S { int x; } ref int func(ref S s) { return s.x; } ref T foo() { S s; return func(s); // escaping reference } Since S cannot implicitly convert to int, it would appear that this circumvents escaping reference detection.No, because under this proposal, s.x is treated as if it were just s as far a scope rules are concerned.
Dec 04 2014
On 12/4/2014 1:52 PM, H. S. Teoh via Digitalmars-d wrote:On Thu, Dec 04, 2014 at 01:10:31PM -0800, Walter Bright via Digitalmars-d wrote:Looks like Andrei Hath Spaketh!On 12/4/2014 11:41 AM, H. S. Teoh via Digitalmars-d wrote:Recently there was a bug filed by Andrei himself, and a bunch of merged PRs, that renamed "type constructor" to "type qualifier". The general sense I got was that we're deprecating "type constructor" in preference for "type qualifier".Can we pretty please use the term "type qualifier" instead of "type constructor"? ;-)I think "type constructor" is the more pedantically correct term, but you'll have to ask Andrei :-)One is to interface with legacy code that does not use 'scope' properly in its declarations. There are probably more.Because there are inevitably cases where you'll need to wrap unsafe code with a safe interface. By screwing this down too tightly for system code, you'll force people to use really ugly workarounds.Are there any use cases for bypassing scope semantics in system code?How common are they?I don't know.Maybe such code *should* look ugly -- to draw attention to the fact that something un- safe is going on.system is ugly enough.Or perhaps we can make cast() strip away scope? I much rather require explicit annotation for potentially unsafe operations, than to have subtle bugs creep into the code just because I forgot to mark something as safe.Start your modules with: safe:There are, though probably incomplete, and there's also an ongoing stream of Phobos PR's for marking things safe that ought not to be system. There's a *lot* of Phobos code that needs to be cleaned up in this way before this can be workable. (Unless we abuse trusted as a temporary workaround -- but that's not an approach I'd like to recommend!)As long as there's ongoing progress!Still not sure how it would address the RC folks' request for compiler support, but I suppose that's a different topic.Yup, different issue.I think it's a fairly important issue, since making this work correctly will also open up optimization opportunities for a good chunk of delegate-based code.See my other reply with resolution.I was thinking more of what implications may hold in the loop body for references to (local) variables outside the loop, since that presents another potential optimization opportunity if we do it right.Yes. I think there are a number of optimization properties we can exploit over time.Ok.Overloaded operators are treated like calls to the functions that back them.OK. You might want to state this explicitly in the DIP, just so it isn't left up to interpretation. :-)That's right, so the compiler assumes it does, unless the parameter is marked as scope.[...] I'm not quite sure I see how this could be implemented. When the compiler is compiling func(), say it's in a different module, there's nothing about the function that says it's illegal to return a reference to the member of an incoming argument.Finally, the following isn't directly related to this DIP, since scope is intended to solve this problem, but I thought I should bring it up. :-) In the section "escaping via return", 5 points are listed as sufficient for detecting the "return func(t);" case. The following section "scope ref" states that these 5 points are "correct" (in implementing escaping reference detection). However, isn't the following a loophole? struct S { int x; } ref int func(ref S s) { return s.x; } ref T foo() { S s; return func(s); // escaping reference } Since S cannot implicitly convert to int, it would appear that this circumvents escaping reference detection.No, because under this proposal, s.x is treated as if it were just s as far a scope rules are concerned.First of all, this isn't a template function so scope inference isn't in effect; secondly, even of it were in effect (say we write it as `func()(ref S s)` instead), it wouldn't be able to infer the return value as scope, since it's obviously escaping a reference to a function argument.'scope ref' parameters cannot be returned by 'ref' or by 'scope ref'. The only distinction between 'ref' and 'scope ref' parameters is the latter cannot be returned by 'ref' or 'scope ref'.So, unless I'm missing something obvious, func() will be accepted by the compiler (as it should, since we didn't mark anything as scope here).The compiler assumes func(s) returns a ref to s, and so it won't allow func(s) to be returned from foo().Now when the compiler compiles foo(), it can no longer see what's inside func(), so there's no way for it to know that the ref int being returned actually comes from the ref S parameter.It assumes it does, unless 'ref S' is marked 'scope ref S'. It's the whole point of 'scope ref'.
Dec 04 2014
On Thu, Dec 04, 2014 at 03:26:32PM -0800, Walter Bright via Digitalmars-d wrote:On 12/4/2014 1:52 PM, H. S. Teoh via Digitalmars-d wrote:[...]Not when all code is system by default!Are there any use cases for bypassing scope semantics in system code?One is to interface with legacy code that does not use 'scope' properly in its declarations. There are probably more.How common are they?I don't know.Maybe such code *should* look ugly -- to draw attention to the fact that something un- safe is going on.system is ugly enough.I would, except that Phobos limitations preclude that. Someone else has already posted the list of bugs, so I won't repeat that here. Needless to say, it's still a ways off before putting safe: at the top of your modules will solve the problem. [...]Or perhaps we can make cast() strip away scope? I much rather require explicit annotation for potentially unsafe operations, than to have subtle bugs creep into the code just because I forgot to mark something as safe.Start your modules with: safe:Ah, I see. So you're saying that under this DIP, `return func(...);` from a ref function will be prohibited unless the arguments to func() were all either scope or non-reference? That sounds good, though it *is* pretty invasive, as any existing ref functions that do this will need to be revised, which potentially spreads 'scope' to many other places, depending on how deeply the chain of tail recursion / tail func calls go.That's right, so the compiler assumes it does, unless the parameter is marked as scope.[...] I'm not quite sure I see how this could be implemented. When the compiler is compiling func(), say it's in a different module, there's nothing about the function that says it's illegal to return a reference to the member of an incoming argument.struct S { int x; } ref int func(ref S s) { return s.x; } ref T foo() { S s; return func(s); // escaping reference } Since S cannot implicitly convert to int, it would appear that this circumvents escaping reference detection.No, because under this proposal, s.x is treated as if it were just s as far a scope rules are concerned.First of all, this isn't a template function so scope inference isn't in effect; secondly, even of it were in effect (say we write it as `func()(ref S s)` instead), it wouldn't be able to infer the return value as scope, since it's obviously escaping a reference to a function argument.'scope ref' parameters cannot be returned by 'ref' or by 'scope ref'. The only distinction between 'ref' and 'scope ref' parameters is the latter cannot be returned by 'ref' or 'scope ref'.So, unless I'm missing something obvious, func() will be accepted by the compiler (as it should, since we didn't mark anything as scope here).The compiler assumes func(s) returns a ref to s, and so it won't allow func(s) to be returned from foo().Got it, thanks. T -- Shin: (n.) A device for finding furniture in the dark.Now when the compiler compiles foo(), it can no longer see what's inside func(), so there's no way for it to know that the ref int being returned actually comes from the ref S parameter.It assumes it does, unless 'ref S' is marked 'scope ref S'. It's the whole point of 'scope ref'.
Dec 04 2014
On 12/4/2014 3:54 PM, H. S. Teoh via Digitalmars-d wrote:Right.The compiler assumes func(s) returns a ref to s, and so it won't allow func(s) to be returned from foo().Ah, I see. So you're saying that under this DIP, `return func(...);` from a ref function will be prohibited unless the arguments to func() were all either scope or non-reference?That sounds good, though it *is* pretty invasive, as any existing ref functions that do this will need to be revised, which potentially spreads 'scope' to many other places, depending on how deeply the chain of tail recursion / tail func calls go.I know: 1. that's why the extensive support for scope inference 2. I couldn't see any other way
Dec 04 2014
On Thu, Dec 04, 2014 at 04:33:22PM -0800, Walter Bright via Digitalmars-d wrote:On 12/4/2014 3:54 PM, H. S. Teoh via Digitalmars-d wrote:I'm not saying it's necessary a bad thing... just that it will be an invasive change that we should be prepared to deal with. T -- English is useful because it is a mess. Since English is a mess, it maps well onto the problem space, which is also a mess, which we call reality. Similarly, Perl was designed to be a mess, though in the nicest of all possible ways. -- Larry WallRight.The compiler assumes func(s) returns a ref to s, and so it won't allow func(s) to be returned from foo().Ah, I see. So you're saying that under this DIP, `return func(...);` from a ref function will be prohibited unless the arguments to func() were all either scope or non-reference?That sounds good, though it *is* pretty invasive, as any existing ref functions that do this will need to be revised, which potentially spreads 'scope' to many other places, depending on how deeply the chain of tail recursion / tail func calls go.I know: 1. that's why the extensive support for scope inference 2. I couldn't see any other way
Dec 04 2014
On Thu, Dec 04, 2014 at 01:52:09PM -0800, H. S. Teoh via Digitalmars-d wrote:On Thu, Dec 04, 2014 at 01:10:31PM -0800, Walter Bright via Digitalmars-d wrote:[...] FYI: https://issues.dlang.org/show_bug.cgi?id=13671 T -- "A man's wife has more power over him than the state has." -- Ralph EmersonOn 12/4/2014 11:41 AM, H. S. Teoh via Digitalmars-d wrote:Recently there was a bug filed by Andrei himself, and a bunch of merged PRs, that renamed "type constructor" to "type qualifier". The general sense I got was that we're deprecating "type constructor" in preference for "type qualifier".Can we pretty please use the term "type qualifier" instead of "type constructor"? ;-)I think "type constructor" is the more pedantically correct term, but you'll have to ask Andrei :-)
Dec 04 2014
On 12/4/2014 1:56 PM, H. S. Teoh via Digitalmars-d wrote:FYI: https://issues.dlang.org/show_bug.cgi?id=13671Ok, ok, I fixed the DIP :-)
Dec 04 2014
On 12/4/2014 1:10 PM, Walter Bright wrote:On 12/4/2014 11:41 AM, H. S. Teoh via Digitalmars-d wrote:Turns out, 'ref' has exactly the same issue. The resolution is the same: alias ref int delegate() dg_t; // ref applies to return type void foo(dg_t dg) { static int i; ref int bar() { return i; } // ref applies to return type dg = &bar; dg() = 3; } versus: void foo(ref int deletate() dg) { // ref applies to declaration dg int bar() { return 3; } dg = &bar; } Replace 'ref' with 'scope'.3) What does scope mean for delegate parameters? To what does the scope apply, the delegate itself, its body, or its return value, or ...? struct S { void opApply(scope void delegate(ref int) loopBody) { ... // what restrictions (might) apply here w.r.t. // how loopBody can be called? } }Hmmm, looks like a problem I didn't think of. darnit!
Dec 04 2014
Was the goal of the post to represent the new alias syntax ? If so, big fail, Walter,...Hmmm, looks like a problem I didn't think of. darnit!Turns out, 'ref' has exactly the same issue. The resolution is the same: alias ref int delegate() dg_t; // ref applies to return type
Dec 04 2014
On 12/4/2014 3:58 PM, Basile Burg wrote:Was the goal of the post to represent the new alias syntax ???
Dec 04 2014
On Thursday, 4 December 2014 at 09:25:11 UTC, Walter Bright wrote:http://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape. This, among other things, makes a ref counting type practical. It also makes it more practical to use other storage allocation schemes than garbage collection. It does not make scope into a type constructor, nor a general type-annotation system. It does not provide an ownership system, though it would complement one.So as mentioned, there are various problem with this DIP : - rvalue are defined as having a scope that goes to the end of the statement. That mean they can never be assigned to anything as per spec. - It add more special casing with & (as if it wasn't enough of a mess with property, optional () and the fact the function aren't first class). For instance *e has infinite lifetime when &(*e) is lifetime(e). Also, considering *e has infinite lifetime, (but not e[i] ???) what is the point of scope at all ? If all indirection goes to infinite lifetime (ie GC) then lifetime only apply to local variable and it should not be able to escape these, scope or not. During discussion, I proposed to differentiate lifetime calculation between lvalues and rvalues (which are inherently different beasts with different lifetime) and carry (or not) the scope flag with each expression.
Dec 04 2014
On 12/4/2014 3:04 PM, deadalnix wrote:So as mentioned, there are various problem with this DIP : - rvalue are defined as having a scope that goes to the end of the statement. That mean they can never be assigned to anything as per spec.I don't believe this is correct. Rvalues can be assigned, just like: __gshared int x; { int i; x = i; } i's scope ends at the } but it can still be assigned to x.- It add more special casing with & (as if it wasn't enough of a mess with property, optional () and the fact the function aren't first class). For instance *e has infinite lifetime when &(*e) is lifetime(e).That's right. I know you're worried about that, but I still don't see it as an actual problem. (The optimizer makes use of this special case all the time.)Also, considering *e has infinite lifetime, (but not e[i] ???)e[i] should be same as *e, unless e is a literal or tuple.what is the point of scope at all ? If all indirection goes to infinite lifetime (ie GC) then lifetime only apply to local variable and it should not be able to escape these, scope or not.I originally had scope only apply to ref, but that made having scoped classes impossible.During discussion, I proposed to differentiate lifetime calculation between lvalues and rvalues (which are inherently different beasts with different lifetime) and carry (or not) the scope flag with each expression.I'm not sure how that would be different from the DIP as it stands now.
Dec 04 2014
On Thu, Dec 04, 2014 at 04:31:18PM -0800, Walter Bright via Digitalmars-d wrote:On 12/4/2014 3:04 PM, deadalnix wrote:[...] Well, the "Expressions" section defines certain lifetimes to be infinity, however, it contradicts the definition of an rvalue's lifetime given under "Definitions" - "Lifetime", which states that an rvalue's lifetime lasts only to the end of the statement. This would imply that the following ought to be rejected: int x = 1 + 2; // lifetime(1 + 2) = end of statement // but lifetime(x) = infinity Sounds like the Definitions section is not precise enough. :-) T -- Curiosity kills the cat. Moral: don't be the cat.So as mentioned, there are various problem with this DIP : - rvalue are defined as having a scope that goes to the end of the statement. That mean they can never be assigned to anything as per spec.I don't believe this is correct. Rvalues can be assigned, just like: __gshared int x; { int i; x = i; } i's scope ends at the } but it can still be assigned to x.
Dec 04 2014
On 12/4/2014 4:48 PM, H. S. Teoh via Digitalmars-d wrote:Well, the "Expressions" section defines certain lifetimes to be infinity, however, it contradicts the definition of an rvalue's lifetime given under "Definitions" - "Lifetime", which states that an rvalue's lifetime lasts only to the end of the statement. This would imply that the following ought to be rejected: int x = 1 + 2; // lifetime(1 + 2) = end of statement // but lifetime(x) = infinity Sounds like the Definitions section is not precise enough. :-)An 'int' is not a view!
Dec 04 2014
On Friday, 5 December 2014 at 00:32:32 UTC, Walter Bright wrote:On 12/4/2014 3:04 PM, deadalnix wrote:It work even better when i has indirections.So as mentioned, there are various problem with this DIP : - rvalue are defined as having a scope that goes to the end of the statement. That mean they can never be assigned to anything as per spec.I don't believe this is correct. Rvalues can be assigned, just like: __gshared int x; { int i; x = i; } i's scope ends at the } but it can still be assigned to x.Yes, this is the job of the optimizer to do this kind of stunt. Not the semantic analysis.- It add more special casing with & (as if it wasn't enough of a mess with property, optional () and the fact the function aren't first class). For instance *e has infinite lifetime when &(*e) is lifetime(e).That's right. I know you're worried about that, but I still don't see it as an actual problem. (The optimizer makes use of this special case all the time.)Ho I missed the *. Sorry for that.Also, considering *e has infinite lifetime, (but not e[i] ???)e[i] should be same as *e, unless e is a literal or tuple.Promoting scoped class on stack is an ownership problem, and out of scope (!). It make sense to allow it as an optimization. Problem is, lifetime goes to infinite after indirection, so I'm not sure what the guarantee is.what is the point of scope at all ? If all indirection goes to infinite lifetime (ie GC) then lifetime only apply to local variable and it should not be able to escape these, scope or not.I originally had scope only apply to ref, but that made having scoped classes impossible.I cause everything reached through the view to be scope and obliviate the need for things like &(*e) having special meaning.During discussion, I proposed to differentiate lifetime calculation between lvalues and rvalues (which are inherently different beasts with different lifetime) and carry (or not) the scope flag with each expression.I'm not sure how that would be different from the DIP as it stands now.
Dec 04 2014
On 12/4/2014 6:56 PM, deadalnix wrote:On Friday, 5 December 2014 at 00:32:32 UTC, Walter Bright wrote:I understand what you're driving at, but only a scoped rvalue would not be copyable.On 12/4/2014 3:04 PM, deadalnix wrote:It work even better when i has indirections.So as mentioned, there are various problem with this DIP : - rvalue are defined as having a scope that goes to the end of the statement. That mean they can never be assigned to anything as per spec.I don't believe this is correct. Rvalues can be assigned, just like: __gshared int x; { int i; x = i; } i's scope ends at the } but it can still be assigned to x.I don't see any other way, nor do I see the practical problem.Yes, this is the job of the optimizer to do this kind of stunt. Not the semantic analysis.- It add more special casing with & (as if it wasn't enough of a mess with property, optional () and the fact the function aren't first class). For instance *e has infinite lifetime when &(*e) is lifetime(e).That's right. I know you're worried about that, but I still don't see it as an actual problem. (The optimizer makes use of this special case all the time.)The guarantee is there will be no references to the class instance after the scoped class goes out of scope.I originally had scope only apply to ref, but that made having scoped classes impossible.Promoting scoped class on stack is an ownership problem, and out of scope (!). It make sense to allow it as an optimization. Problem is, lifetime goes to infinite after indirection, so I'm not sure what the guarantee is.Are you suggesting transitive scope?I cause everything reached through the view to be scope and obliviate the need for things like &(*e) having special meaning.During discussion, I proposed to differentiate lifetime calculation between lvalues and rvalues (which are inherently different beasts with different lifetime) and carry (or not) the scope flag with each expression.I'm not sure how that would be different from the DIP as it stands now.
Dec 05 2014
On Friday, 5 December 2014 at 21:32:53 UTC, Walter Bright wrote:The DIP say nothing about scoped rvalue having different behavior than non scoped ones.I understand what you're driving at, but only a scoped rvalue would not be copyable.I don't believe this is correct. Rvalues can be assigned, just like: __gshared int x; { int i; x = i; } i's scope ends at the } but it can still be assigned to x.It work even better when i has indirections.Through use of that view. I see it as follow: - When unconsumed owner goes out of scope, it can (must ?) be free automatically. - scope uses do not consume. - When the compiler see a pair of alloc/free, it can promote on stack. That is a much more useful definition as it allow for stack promotion after inlining: class FooBuilder { Foo build() { return new Foo(); } } class Foo {} void bar() { auto f = new FooBuilder().build(); // Use f and do not consume... } This can be reduced in such a way no allocation happen at all.I originally had scope only apply to ref, but that madeThe guarantee is there will be no references to the class instance after the scoped class goes out of scope.having scoped classes impossible.Promoting scoped class on stack is an ownership problem, and out of scope (!). It make sense to allow it as an optimization. Problem is, lifetime goes to infinite after indirection, so I'm not sure what the guarantee is.For rvalues, yes. Not for lvalues.I cause everything reached through the view to be scope and obliviate the need for things like &(*e) having special meaning.Are you suggesting transitive scope?
Dec 05 2014
On 12/5/2014 3:59 PM, deadalnix wrote:On Friday, 5 December 2014 at 21:32:53 UTC, Walter Bright wrote:Can you propose some new wording?The DIP say nothing about scoped rvalue having different behavior than non scoped ones.I understand what you're driving at, but only a scoped rvalue would not be copyable.I don't believe this is correct. Rvalues can be assigned, just like: __gshared int x; { int i; x = i; } i's scope ends at the } but it can still be assigned to x.It work even better when i has indirections.Yes, but I think the proposal allows for that.Through use of that view. I see it as follow: - When unconsumed owner goes out of scope, it can (must ?) be free automatically. - scope uses do not consume. - When the compiler see a pair of alloc/free, it can promote on stack. That is a much more useful definition as it allow for stack promotion after inlining: class FooBuilder { Foo build() { return new Foo(); } } class Foo {} void bar() { auto f = new FooBuilder().build(); // Use f and do not consume... } This can be reduced in such a way no allocation happen at all.I originally had scope only apply to ref, but that madeThe guarantee is there will be no references to the class instance after the scoped class goes out of scope.having scoped classes impossible.Promoting scoped class on stack is an ownership problem, and out of scope (!). It make sense to allow it as an optimization. Problem is, lifetime goes to infinite after indirection, so I'm not sure what the guarantee is.I don't think that is workable.For rvalues, yes. Not for lvalues.I cause everything reached through the view to be scope and obliviate the need for things like &(*e) having special meaning.Are you suggesting transitive scope?
Dec 06 2014
On Saturday, 6 December 2014 at 21:57:03 UTC, Walter Bright wrote:On 12/5/2014 3:59 PM, deadalnix wrote:I did: An infinite lifetime is a lifetime greater or equal than any other lifetime. Expression of infinite lifetime are: - literals - GC heap allocated objects - statics and enums. - rvalues of type that do not contain indirections. - non scope rvalues. Dereference share the lifetime of the dereferenced expression (ie infinite lifetime unless the expression is scope). Address of expression shared the lifetime of the base expression, and in addition gain the scope flag.The DIP say nothing about scoped rvalue having different behavior than non scoped ones.Can you propose some new wording?That is the only way. For rvalue, you got to take the lower possible lifetime, but for lvalue, you want to consider the highest possible lifetime.I don't think that is workable.Are you suggesting transitive scope?For rvalues, yes. Not for lvalues.
Dec 08 2014
On 12/4/14 4:24 AM, Walter Bright wrote:http://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape. This, among other things, makes a ref counting type practical. It also makes it more practical to use other storage allocation schemes than garbage collection. It does not make scope into a type constructor, nor a general type-annotation system. It does not provide an ownership system, though it would complement one.Can we take a step back here? I read many people's comments and I understand only about half of them. Can someone who knows what this new feature is supposed to do give some Ali Çehreli-like description on the feature? Basically, let's strip out the *proof* in the DIP (the how it works and why we have it), and focus on how it is to be used. I still am having a hard time wrapping my head around the benefits and when to use scope, scope ref, why I would use it. I'm worried that we are adding all this complication and it will confuse the shit out of users, to the point where they won't use it. -Steve
Dec 05 2014
On 12/5/2014 7:27 AM, Steven Schveighoffer wrote:Can someone who knows what this new feature is supposed to do give some Ali Çehreli-like description on the feature? Basically, let's strip out the *proof* in the DIP (the how it works and why we have it), and focus on how it is to be used. I still am having a hard time wrapping my head around the benefits and when to use scope, scope ref, why I would use it. I'm worried that we are adding all this complication and it will confuse the shit out of users, to the point where they won't use it.The tl;dr version is when a declaration is tagged with 'scope', the contents of that variable will not escape the lifetime of that declaration. It means that this code will be safe: void foo(scope int* p); p = malloc(n); foo(p); free(p); The rest is all the nuts and bolts of making that work.
Dec 05 2014
On Friday, 5 December 2014 at 20:55:55 UTC, Walter Bright wrote:On 12/5/2014 7:27 AM, Steven Schveighoffer wrote:<brainstorm> What about also adding the inverse of scope? Then scope can be inferred. As in: ``` void foo(int* p); void free(P)(consume P* p); p = malloc(n); foo(p); // here foo() gets scope, since free consumes it. free(p); ``` So you do not need to write scope everywhere. And it would prevent this code: ``` { free(p); free(p); // Error: p already consumed } ``` </brainstorm>Can someone who knows what this new feature is supposed to do give some Ali Çehreli-like description on the feature? Basically, let's strip out the *proof* in the DIP (the how it works and why we have it), and focus on how it is to be used. I still am having a hard time wrapping my head around the benefits and when to use scope, scope ref, why I would use it. I'm worried that we are adding all this complication and it will confuse the shit out of users, to the point where they won't use it.The tl;dr version is when a declaration is tagged with 'scope', the contents of that variable will not escape the lifetime of that declaration. It means that this code will be safe: void foo(scope int* p); p = malloc(n); foo(p); free(p); The rest is all the nuts and bolts of making that work.
Dec 05 2014
On Saturday, 6 December 2014 at 04:31:48 UTC, Sebastiaan Koppe wrote:What about also adding the inverse of scope? Then scope can be inferred. As in: ``` void foo(int* p); void free(P)(consume P* p);Yes, this is much better. When I suggested it, it was rejected because D is too concerned about breaking existing code. Which is a not-very-good argument since this breaking change is concervative (you only have to add "consume" or something similar when the compiler complains). The obvious solution is to do as you suggest and in addition do all safe analysis on a high level IR layer using dataflow through and through. Instead D continues down the rather flimsy path of partially addressing these issues in the type system… which will lead to a more complicated and less complete solution where safe basically continues to be a leaky cauldron…
Dec 06 2014
On Saturday, 6 December 2014 at 12:38:24 UTC, Ola Fosheim Grøstad wrote:On Saturday, 6 December 2014 at 04:31:48 UTC, Sebastiaan Koppe wrote:Hmm, I see. Whenever I suggest to break something at my job - which, admittedly is much easier there than in a language - I get faced with some serious reluctance. My argument is always the same: It is going to make you happy, eventually. How would it break anything though? Wouldn't functions eligible for `consume` already have the programmer ensuring the arguments haven't escaped before/after the function call? In case they did a bad job - sure it would break - but it would have been a bug.What about also adding the inverse of scope? Then scope can be inferred. As in: ``` void foo(int* p); void free(P)(consume P* p);Yes, this is much better. When I suggested it, it was rejected because D is too concerned about breaking existing code. Which is a not-very-good argument since this breaking change is concervative (you only have to add "consume" or something similar when the compiler complains).The obvious solution is to do as you suggest and in addition do all safe analysis on a high level IR layer using dataflow through and through.I am a big proponent of dataflow analyses, but I got the feeling people think is it pretty hard. Couldn't find much detailed papers on it, so I don't know.
Dec 06 2014
On Sunday, 7 December 2014 at 06:52:38 UTC, Sebastiaan Koppe wrote:On Saturday, 6 December 2014 at 12:38:24 UTC, Ola Fosheim Grøstad wrote:On Saturday, 6 December 2014 at 04:31:48 UTC, Sebastiaan Koppe wrote:I am a big proponent of dataflow analyses, but I got the feeling people think is it pretty hard.I think D is in a good position to use the information available in assert* and in contracts for that. Other languages have to use dedicated tools for that. In a latter step, D could even formalize this into, as discussed in other threads, VRP sub-language. But, for the time being, the leverage offered by assert and in /out is available there to be used. *yes, I know the war between assert and assume
Dec 07 2014
On Sunday, 7 December 2014 at 06:52:38 UTC, Sebastiaan Koppe wrote:How would it break anything though? Wouldn't functions eligible for `consume` already have the programmer ensuring the arguments haven't escaped before/after the function call? In case they did a bad job - sure it would break - but it would have been a bug.I don't think the breakage is a serious problem in this case, so I obviously agree with you… But, the deep-rooted problem is that you actually need different properties associated with pointers to do this properly: unique, single-threaded shared, multi-threaded shared, gc, non-owning… And this could be resolved with templates and having the compiler recognize pointer resolutions that lead to the same lower level code (to avoid bloat). But the D authors don't want ownership related pointertypes… and I can't see that work.I am a big proponent of dataflow analyses, but I got the feeling people think is it pretty hard. Couldn't find much detailed papers on it, so I don't know.You can do it in a simpler and conservative fashion and accept false positives (or negatives) so that you here-and-there have to inject annotations like this: "trust me I have proven that this property holds here", then let the compiler infer the rest. With a good IDE this should be no biggie: just inject suggestions in the code and let the programmer confirm/refine them. If you want to guarantee memory-safety you should do it with a single uniform mechanism, trying to do it bit-by-bit special casing is not a good idea. So given the current approach, I'm inclined to turn off safe, make everything nogc, write my own libraries with unique_ptr/shared_ptr and call it a day… The current approach does not address situations where I run into memory related bugs in my own code.
Dec 07 2014
On Saturday, 6 December 2014 at 12:38:24 UTC, Ola Fosheim Grøstad wrote:On Saturday, 6 December 2014 at 04:31:48 UTC, Sebastiaan Koppe wrote:This is inherently about ownership. I have a proposal about this. Scope is about using things without ownership. Both are linked but different beast.What about also adding the inverse of scope? Then scope can be inferred. As in: ``` void foo(int* p); void free(P)(consume P* p);Yes, this is much better. When I suggested it, it was rejected because D is too concerned about breaking existing code. Which is a not-very-good argument since this breaking change is concervative (you only have to add "consume" or something similar when the compiler complains). The obvious solution is to do as you suggest and in addition do all safe analysis on a high level IR layer using dataflow through and through. Instead D continues down the rather flimsy path of partially addressing these issues in the type system… which will lead to a more complicated and less complete solution where safe basically continues to be a leaky cauldron…
Dec 08 2014
On Monday, 8 December 2014 at 23:00:05 UTC, deadalnix wrote:This is inherently about ownership. I have a proposal about this. Scope is about using things without ownership. Both are linked but different beast.Not really different. The activation record (stack frame) is conceptually an object. Scoped objects are owned by the activation record. You have the same problem when handing out references to parts of composite objects. When propagating references down a call chain you can keep track of the associated call-depth, when the call-depth associated with the reference is greater than 0, then it safe to return the reference from a function. Right?
Dec 09 2014
On Tuesday, 9 December 2014 at 19:39:31 UTC, Ola Fosheim Grøstad wrote:On Monday, 8 December 2014 at 23:00:05 UTC, deadalnix wrote:That why i say they are linked. I don't think your way of stating it contradict what I said. scope allow for manipulation of data without owning them. Whatever the owner is (be it the stack frame or anything else) doesn't really matter here.This is inherently about ownership. I have a proposal about this. Scope is about using things without ownership. Both are linked but different beast.Not really different. The activation record (stack frame) is conceptually an object. Scoped objects are owned by the activation record. You have the same problem when handing out references to parts of composite objects. When propagating references down a call chain you can keep track of the associated call-depth, when the call-depth associated with the reference is greater than 0, then it safe to return the reference from a function. Right?
Dec 09 2014
On Tuesday, 9 December 2014 at 21:58:48 UTC, deadalnix wrote:That why i say they are linked. I don't think your way of stating it contradict what I said. scope allow for manipulation of data without owning them. Whatever the owner is (be it the stack frame or anything else) doesn't really matter here.It does when you return parts of it, like if you pass in a binary tree and return a node. Which you have to be able to do for the concept to make sense. When you solve issues in language design you should address the hard issues first, because the easy issues will then tend to resolve themselves if the design is good. If you start with "the low hanging fruit" you end up with pointless special casing… D is already in that landscape and should try hard not to sink deeper into the muddy waters. I posit that if you have a solution for ownership/retaining references then the solution for scope will come from this as a side-effect.
Dec 10 2014
On Wednesday, 10 December 2014 at 15:15:59 UTC, Ola Fosheim Grøstad wrote:On Tuesday, 9 December 2014 at 21:58:48 UTC, deadalnix wrote:D is already in that landscape and should try hard not to sink deeper into the muddy waters.Funny thing it is that it started exactly as a reaction to those muddy waters. I believe this is the "Stroustrup curse": " Much of the relative simplicity of Java is - like for most new languages - partly an illusion and partly a function of its incompleteness. As time passes, Java will grow significantly in size and complexity. It will double or triple in size and grow implementation-dependent extensions or libraries. That is the way every commercially successful language has developed. Just look at any language you consider successful on a large scale. I know of no exceptions, and there are good reasons for this phenomenon. [I wrote this before 2000; now (2012), the language part of the Java 7 specification is slightly longer in terms of number of pages than the ISO C++11 language specification.] "
Dec 10 2014
On Wednesday, 10 December 2014 at 16:17:16 UTC, eles wrote:I believe this is the "Stroustrup curse": " Much of the relative simplicity of Java is - like for most new languages - partly an illusion and partly a function of its incompleteness. As time passes, Java will grow significantly in size and complexity. It will double or triple in size and grow implementation-dependent extensions or libraries. That is the way every commercially successful language has developed. Just look at any language you consider successful on a large scale.I think he is making excuses for himself. Both Java and C++ are rather close to Simula+C as a starting point and have added cruft that should have been predicted in the initial version. Anyway, I think language specs often are too verbose, they probably all hit around 700-1000 pages eventually due to editors cutting it back to that book-like size before it is published (and when the book-sized limit has been reached they feel they have done enough ungrateful and unpaid work so they don't cut more even though they could have).
Dec 10 2014
On Wednesday, 10 December 2014 at 15:15:59 UTC, Ola Fosheim Grøstad wrote:On Tuesday, 9 December 2014 at 21:58:48 UTC, deadalnix wrote:That is completely off topic. this is a function parameter, you can return scope, and you don't need to know who own the container for that to work. Granted, the current proposal lack the horsepower to do so.That why i say they are linked. I don't think your way of stating it contradict what I said. scope allow for manipulation of data without owning them. Whatever the owner is (be it the stack frame or anything else) doesn't really matter here.It does when you return parts of it, like if you pass in a binary tree and return a node. Which you have to be able to do for the concept to make sense. When you solve issues in language design you should address the hard issues first, because the easy issues will then tend to resolve themselves if the design is good. If you start with "the low hanging fruit" you end up with pointless special casing… D is already in that landscape and should try hard not to sink deeper into the muddy waters. I posit that if you have a solution for ownership/retaining references then the solution for scope will come from this as a side-effect.
Dec 10 2014
On Wednesday, 10 December 2014 at 22:05:10 UTC, deadalnix wrote:That is completely off topic. this is a function parameter, you can return scope, and you don't need to know who own the container for that to work.You have many scopes, if two different scopes pass in "horses" to a function in a recursive chain then you need to know which horse the returned "leg" belongs to… But by all means… go ahead if you think it is off topic. (it isn't)
Dec 10 2014
On Wednesday, 10 December 2014 at 22:20:37 UTC, Ola Fosheim Grøstad wrote:On Wednesday, 10 December 2014 at 22:05:10 UTC, deadalnix wrote:It is always safe to consider scopeness of the retrun value (if marked scope) as being the intersection of the lifetime of parameters. That should cover most bases, and we can still extends later if this is too limited (I suspect it is ok for most cases).That is completely off topic. this is a function parameter, you can return scope, and you don't need to know who own the container for that to work.You have many scopes, if two different scopes pass in "horses" to a function in a recursive chain then you need to know which horse the returned "leg" belongs to… But by all means… go ahead if you think it is off topic. (it isn't)
Dec 10 2014
deadalnix:That should cover most bases, and we can still extends later if this is too limited (I suspect it is ok for most cases).What syntax do you suggest to use if you want to extend it that way later? Bye, bearophile
Dec 10 2014
On Thursday, 11 December 2014 at 00:48:32 UTC, bearophile wrote:deadalnix:scope[symbol_name] would get the same lifetime as symbol_name for instance. This is only a proposal, I'm not convinced that this is strictly necessary, and, as long as we know we can add it if needed, we are good.That should cover most bases, and we can still extends later if this is too limited (I suspect it is ok for most cases).What syntax do you suggest to use if you want to extend it that way later? Bye, bearophile
Dec 10 2014
On Thursday, 11 December 2014 at 00:35:46 UTC, deadalnix wrote:It is always safe to consider scopeness of the retrun value (if marked scope) as being the intersection of the lifetime of parameters. That should cover most bases, and we can still extends later if this is too limited (I suspect it is ok for most cases).Linear typing is already extremely limiting, by limiting it even further you end up with something annoying. You basically get a version of memory safety that does not solve any typical memory unsafe situations. By having pointers that do scope-depth-tracking you do at least get a generic solution that can be optimized away when possible. The D authors have to accept that you need to embed ownership in pointers if you want memory safety and convenience, or that you have to provide means to guide the semantic analysis. You need one or the other, or both, but you cannot pretend that you can do without. Arbitrary constraints are annoying, not convenient. If I as a programmer know that something is safe, then the compiler should accept it, and the language should allow me express it.
Dec 11 2014
On 11/12/2014 09:07, "Ola Fosheim Grøstad" <ola.fosheim.grostad+dlang gmail.com>" wrote:If I as a programmer know that something is safe, then the compiler should accept it, and the language should allow me express it.It will, use system. Maybe this proposal will be supplemented later, but there are always costs to making the language more complicated. We need to be certain they are worth it before adding them. A cautious approach can be extended later, if the situation demands it. Sometimes innovative workarounds are developed that are difficult to foresee in advance - e.g. in Rust their type system can be restrictive, but they rely on trusted library functions/types to make more things possible in a safe way.
Dec 11 2014
On Thursday, 11 December 2014 at 13:43:07 UTC, Nick Treleaven wrote:It will, use system.Yes…Maybe this proposal will be supplemented later, but there are always costs to making the language more complicated. We need to be certain they are worth it before adding them. A cautious approach can be extended later, if the situation demands it.Can it be extended later? Without breaking stuff? You need to get it right from the start, otherwise you end up with N different, but incompatible ways of doing the same thing. That means you need to get ownership right first.
Dec 11 2014
Nick Treleaven:Sometimes innovative workarounds are developed that are difficult to foresee in advance - e.g. in Rust their type system can be restrictive, but they rely on trusted library functions/types to make more things possible in a safe way.Ideally a type system should be flexible enough, in practice giving it a high flexibility has significant costs (in compilation times, amount of code that implements the system, bugs in such implementation, costs for the final programmer in inventing a way to express the semantics, etc), so most languages avoid a too much complex type system (even Haskell does this) and accept reasonable workarounds... Bye, bearophile
Dec 11 2014
On Thursday, 11 December 2014 at 15:37:27 UTC, bearophile wrote:Nick Treleaven:What is important is how much expressiveness you get from the complexity. here we get almost none. Considering complexity alone is not going to yield good results.Sometimes innovative workarounds are developed that are difficult to foresee in advance - e.g. in Rust their type system can be restrictive, but they rely on trusted library functions/types to make more things possible in a safe way.Ideally a type system should be flexible enough, in practice giving it a high flexibility has significant costs (in compilation times, amount of code that implements the system, bugs in such implementation, costs for the final programmer in inventing a way to express the semantics, etc), so most languages avoid a too much complex type system (even Haskell does this) and accept reasonable workarounds... Bye, bearophile
Dec 11 2014
On Thursday, 11 December 2014 at 09:07:44 UTC, Ola Fosheim Grøstad wrote:On Thursday, 11 December 2014 at 00:35:46 UTC, deadalnix wrote:I have no idea what you are saying. It sounds like randomly generated gibberish.It is always safe to consider scopeness of the retrun value (if marked scope) as being the intersection of the lifetime of parameters. That should cover most bases, and we can still extends later if this is too limited (I suspect it is ok for most cases).Linear typing is already extremely limiting, by limiting it even further you end up with something annoying. You basically get a version of memory safety that does not solve any typical memory unsafe situations. By having pointers that do scope-depth-tracking you do at least get a generic solution that can be optimized away when possible. The D authors have to accept that you need to embed ownership in pointers if you want memory safety and convenience, or that you have to provide means to guide the semantic analysis. You need one or the other, or both, but you cannot pretend that you can do without. Arbitrary constraints are annoying, not convenient. If I as a programmer know that something is safe, then the compiler should accept it, and the language should allow me express it.
Dec 11 2014
On Thursday, 11 December 2014 at 23:26:33 UTC, deadalnix wrote:I have no idea what you are saying. It sounds like randomly generated gibberish.What is your problem? Scope parameters are trying to address what Rust tried to do with borrowing. To get an idea of where you are going you should look at ordered linear and non-linear type systems. Without a dynamic solution OR a proof system OR a way to provide a manual guarantee you will hit the ceiling for what you can do, which will be annoying for the user (programmer).
Dec 11 2014
On 12/5/14 3:55 PM, Walter Bright wrote:On 12/5/2014 7:27 AM, Steven Schveighoffer wrote:This is not what I was asking for. What I wanted to know was, when I see scope ref, why is it there? When should I use it? When should I use scope? What nasty things are prevented if I use it? Examples would be preferable. Note, in your example above, marking foo pure solves the problem already. The rules and statements give an inferred benefit. I'd like that benefit to be more fully explained. -SteveCan someone who knows what this new feature is supposed to do give some Ali Çehreli-like description on the feature? Basically, let's strip out the *proof* in the DIP (the how it works and why we have it), and focus on how it is to be used. I still am having a hard time wrapping my head around the benefits and when to use scope, scope ref, why I would use it. I'm worried that we are adding all this complication and it will confuse the shit out of users, to the point where they won't use it.The tl;dr version is when a declaration is tagged with 'scope', the contents of that variable will not escape the lifetime of that declaration. It means that this code will be safe: void foo(scope int* p); p = malloc(n); foo(p); free(p); The rest is all the nuts and bolts of making that work.
Dec 08 2014
On 12/8/2014 7:23 AM, Steven Schveighoffer wrote:What I wanted to know was, when I see scope ref, why is it there? When should I use it?It's there to indicate a parameter is NOT returned by ref.When should I use scope? What nasty things are prevented if I use it? Examples would be preferable.http://wiki.dlang.org/DIP69#Ref has examples of errors that are prevented.
Dec 14 2014
On 05.12.14 21:55, Walter Bright wrote:It means that this code will be safe: void foo(scope int* p); p = malloc(n); foo(p); free(p); The rest is all the nuts and bolts of making that work.If it is main goal, opposite looks more natural. void foo(int* p); void foo1(escape int* p); p = malloc(n); p1 = malloc(n); foo(p); free(p); // OK foo1(p1); free(p1); // no
Dec 08 2014
On 2014-12-05 15:27:41 +0000, Steven Schveighoffer said:On 12/4/14 4:24 AM, Walter Bright wrote:This is exactly why this feature should be default behavior with compiler warnings generated when things escape scope. The basic idea is that most things should be on the stack and go away when the stack goes away. The proposal codifies how the compiler should infer that references to variables which are to be placed on the stack are not escaping their scope. The benefit is that the GC has to do *way* less work from what it has to do now for most of the use cases of D. -Shammahhttp://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape. This, among other things, makes a ref counting type practical. It also makes it more practical to use other storage allocation schemes than garbage collection. It does not make scope into a type constructor, nor a general type-annotation system. It does not provide an ownership system, though it would complement one.Can we take a step back here? I read many people's comments and I understand only about half of them. Can someone who knows what this new feature is supposed to do give some Ali Çehreli-like description on the feature? Basically, let's strip out the *proof* in the DIP (the how it works and why we have it), and focus on how it is to be used. I still am having a hard time wrapping my head around the benefits and when to use scope, scope ref, why I would use it. I'm worried that we are adding all this complication and it will confuse the shit out of users, to the point where they won't use it. -Steve
Dec 10 2014
On 11.12.14 08:25, Shammah Chancellor wrote:This is exactly why this feature should be default behavior with compiler warnings generated when things escape scope.It can be compiler switch escape=I(gnore)|W(arning)|E(rror).
Dec 11 2014
There are limitations this proposal has in comparison to my original one. These limitations might of course be harmless and play no role in practice, but on the other hand, they may, so I think it's good to list them here. Additionally I have to agree with Steven Schveighoffer: This DIP is very complicated to understand. It's not obvious how the various parts play together, and why/to which degree it "works", and which are the limitations. I don't think that's only because my brain is already locked on my proposal... 1) Escape detection is limited to `ref`. T* evil; ref T func(scope ref T t, ref T u) safe { return t; // Error: escaping scope ref t return u; // ok evil = &u; // Error: escaping reference } vs. T[] evil; T[] func(scope T[] t, T[] u) safe { return t; // Error: cannot return scope return u; // ok evil = u; // !!! not good } As can be seen, `ref T u` is protected from escaping (apart from returning it), while `T[] u` in the second example is not. There's no general way to express that `u` can only be returned from the function, but will not be retained otherwise by storing it in a global variable. Adding `pure` can express this in many cases, but is, of course, not always possible. Another workaround is passing the parameters as `ref`, but this would introduce an additional indirection and has different semantics (e.g. when the lengths of the slices are modified). 2) `scope ref` return values cannot be stored. scope ref int foo(); void bar(scope ref int a); foo().bar(); // allowed scope tmp = foo(); // not allowed tmp.bar(); Another example: struct Container(T) { scope ref T opIndex(size_t index); } void bar(scope ref int a); Container c; bar(c[42]); // ok scope ref tmp = c[42]; // nope Both cases should be fine theoretically; the "real" owner lives longer than `tmp`. Unfortunately the compiler doesn't know about this. Both restrictions 1) and 2) are because there are no explicit lifetime/owner designations (the scope!identifier thingy in my proposal). 3) `scope` cannot be used for value types. I can think of a few use cases for scoped value types (RC and file descriptors), but they might only be marginal. 4) No overloading on `scope`. This is at least partially a consequence of `scope` inference. I think overloading can be made to work in the presence of inference, but I haven't thought it through. 5) `scope` is a storage class. Manu complained about `ref` being a storage class. If I understand him right, one reason is that we have a large toolkit for dealing with type modifiers, but almost nothing for storage classes. I have to agree with him there. But I haven't understood his point fully, maybe he himself can post more about his problems with this? 6) There seem to be problems with chaining. scope ref int foo(); scope ref int bar1(ref int a) { return a; } scope ref int bar2(scope ref int a) { return a; } ref int bar3(ref int a) { return a; } ref int bar4(scope ref int a) { return a; } void baz(scope ref int a); Which of the following calls would work? foo().bar1().baz(); foo().bar2().baz(); foo().bar3().baz(); foo().bar4().baz(); I'm not sure I understand this fully yet, but it could be that none of them work...
Dec 05 2014
On 12/5/2014 8:48 AM, "Marc Schütz" <schuetzm gmx.net>" wrote:There are limitations this proposal has in comparison to my original one. These limitations might of course be harmless and play no role in practice, but on the other hand, they may, so I think it's good to list them here.Good idea. Certainly, this is less powerful than your proposal. The question, obviously, is what is good enough to get the job done. By "the job", I mean reference counting, migrating many allocations to RAII (meaning the stack), and eliminating a lot of closure GC allocations.Additionally I have to agree with Steven Schveighoffer: This DIP is very complicated to understand. It's not obvious how the various parts play together, and why/to which degree it "works", and which are the limitations. I don't think that's only because my brain is already locked on my proposal...I'm still looking for an easier way to explain it. The good news in this, however, is if it is correctly implement the compiler should be a big help in using scope correctly.1) Escape detection is limited to `ref`. T* evil; ref T func(scope ref T t, ref T u) safe { return t; // Error: escaping scope ref t return u; // ok evil = &u; // Error: escaping reference } vs. T[] evil; T[] func(scope T[] t, T[] u) safe { return t; // Error: cannot return scope return u; // ok evil = u; // !!! not goodright, although: evil = t; // Error: not allowed} As can be seen, `ref T u` is protected from escaping (apart from returning it), while `T[] u` in the second example is not. There's no general way to express that `u` can only be returned from the function, but will not be retained otherwise by storing it in a global variable. Adding `pure` can express this in many cases, but is, of course, not always possible.As you point out, 'ref' is designed for this.Another workaround is passing the parameters as `ref`, but this would introduce an additional indirection and has different semantics (e.g. when the lengths of the slices are modified). 2) `scope ref` return values cannot be stored. scope ref int foo(); void bar(scope ref int a); foo().bar(); // allowed scope tmp = foo(); // not allowed tmp.bar();RightAnother example: struct Container(T) { scope ref T opIndex(size_t index); } void bar(scope ref int a); Container c; bar(c[42]); // ok scope ref tmp = c[42]; // nope Both cases should be fine theoretically; the "real" owner lives longer than `tmp`. Unfortunately the compiler doesn't know about this.Right, though the compiler can optimize to produce the equivalent.Both restrictions 1) and 2) are because there are no explicit lifetime/owner designations (the scope!identifier thingy in my proposal). 3) `scope` cannot be used for value types. I can think of a few use cases for scoped value types (RC and file descriptors), but they might only be marginal.I suspect that one can encapsulate such values in a struct where access to them is strictly controlled.4) No overloading on `scope`. This is at least partially a consequence of `scope` inference. I think overloading can be made to work in the presence of inference, but I haven't thought it through.Right. Different overloads can have different semantic implementations, so what should inference do? I also suspect it is bad style to overload on 'scope'.5) `scope` is a storage class. Manu complained about `ref` being a storage class. If I understand him right, one reason is that we have a large toolkit for dealing with type modifiers, but almost nothing for storage classes. I have to agree with him there. But I haven't understood his point fully, maybe he himself can post more about his problems with this?I didn't fully understand Manu's issue, but it was about 'ref' not being inferred by template type deduction. I didn't understand why 'auto ref' did not work for him. I got the impression that he was trying to program in D the same way he'd do things in C++, and that's where the trouble came in.6) There seem to be problems with chaining. scope ref int foo(); scope ref int bar1(ref int a) { return a; } scope ref int bar2(scope ref int a) { return a; } ref int bar3(ref int a) { return a; } ref int bar4(scope ref int a) { return a; } void baz(scope ref int a); Which of the following calls would work? foo().bar1().baz();yesfoo().bar2().baz();no - cannot return scope ref parameterfoo().bar3().baz();yesfoo().bar4().baz();no, cannot return scope ref parameterI'm not sure I understand this fully yet, but it could be that none of them work...Well, you're half right :-)
Dec 05 2014
Walter Bright:The question, obviously, is what is good enough to get the job done. By "the job", I mean reference counting, migrating many allocations to RAII (meaning the stack), and eliminating a lot of closure GC allocations.Another essential purpose of a similar proposal is to avoid some bugs caused by using memory from vanished stack frames, etc, that is to have memory safety in D. Bye, bearophile
Dec 05 2014
On 6 December 2014 at 09:58, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 12/5/2014 8:48 AM, "Marc Schütz" <schuetzm gmx.net>" wrote:NO!! I barely program C++ at all! I basically write C code with 'enum' and 'class'. NEVER 'template', and very rarely 'virtual'. Your impression is dead wrong. If I do things in any particular 'way', it is that at all times, I have regard for, and control over the code generation, the ABI, and I also value distribution of code as static libs, which means I must retain tight control over where code is, and isn't, emitted. I expect this from a native language. It's exactly as Marc says, we have the best tools for dealing with types of any language I know, and practically none for dealing with 'storage class'. In my use of meta in D, 'ref' is the single greatest cause of complexity, code bloat, duplication, and text mixins. I've been banging on about this for years! I've been over it so many times. I'll start over if there is actually some possibility I can convince you? Is there? I've lost faith that I am able to have any meaningful impact on issues that matter to me, and I'm fairly sure at this point that my compounded resentment and frustration actually discredit my cause, and certainly, my quality of debate. I'm passionate about these things, but I'm also extremely frustrated. To date, whenever I have engaged in a topic that I *really* care about, it goes the other direction. To participate has proven to be something of a (very time consuming) act of masochism. I __absolutely objected__ to 'auro ref' when it appeared. I argued that it was a massive mistake, and since it's introduction and experience with it in the wild, I am more confident in that conviction than ever. As a programmer, I expect control over whether code is a template, or not. 'ref' doesn't have anything to do with templates. Confusing these 2 concepts was such a big mistake. auto ref makes a template out of something that shouldn't be a template. It's a particularly crude hack to address a prior mistake. ref should have been fixed at that time, not compounded with layers of even more weird and special-case/non-uniform behaviours above it. There has been a couple of instances where the situation has been appropriate that I've tried to make use of auto ref, but in each case, the semantics have never been what I want. auto ref presumes to decide for you when something should be a ref or not. It prescribes a bunch of rules on how that decision is made, but it doesn't know what I'm doing, and it gets it wrong. In my experience, auto ref has proven to be, at best, completely useless. But in some cases I've found where I bump into it in 3rd party code, it's been a nuisance, requiring me to wrap it away. ref is a bad design. C++'s design isn't fantastic, and I appreciate that D made effort to improve on it, but we need to recognise when the experiment was a failure. D's design is terrible; it's basically orthogonal to the rest of the language. It's created way more complicated edge cases for me than C++ references ever have. Anyone who says otherwise obviously hasn't really used it much! Don't double down on that mistake with scope. My intended reply to this post was to ask you to justify making it a storage class, and why the design fails as a type constructor? Can we explore that direction to it's point of failure? As a storage class, it runs the risk of doubling out existing bloat caused by ref. As a type constructor, I see no disadvantages. It even addresses some of the awkward problems right at the face of storage classes, like how/where the attributes actually apply. Type constructors use parens; ie, const(T), and scope(T) would make that matter a lot more clear. Apart from the storage class issue, it looks okay, but it gives me the feeling that it kinda stops short. Marc's proposal addressed more issues. I feel this proposal will result in more edge cases than Marc's proposal. The major edge case that I imagine is that since scope return values can't be assigned to scope local's, that will result in some awkward meta, requiring yet more special cases. I think Mark's proposal may be a lot more relaxed in that way.There are limitations this proposal has in comparison to my original one. These limitations might of course be harmless and play no role in practice, but on the other hand, they may, so I think it's good to list them here.Good idea. Certainly, this is less powerful than your proposal. The question, obviously, is what is good enough to get the job done. By "the job", I mean reference counting, migrating many allocations to RAII (meaning the stack), and eliminating a lot of closure GC allocations.Additionally I have to agree with Steven Schveighoffer: This DIP is very complicated to understand. It's not obvious how the various parts play together, and why/to which degree it "works", and which are the limitations. I don't think that's only because my brain is already locked on my proposal...I'm still looking for an easier way to explain it. The good news in this, however, is if it is correctly implement the compiler should be a big help in using scope correctly.1) Escape detection is limited to `ref`. T* evil; ref T func(scope ref T t, ref T u) safe { return t; // Error: escaping scope ref t return u; // ok evil = &u; // Error: escaping reference } vs. T[] evil; T[] func(scope T[] t, T[] u) safe { return t; // Error: cannot return scope return u; // ok evil = u; // !!! not goodright, although: evil = t; // Error: not allowed} As can be seen, `ref T u` is protected from escaping (apart from returning it), while `T[] u` in the second example is not. There's no general way to express that `u` can only be returned from the function, but will not be retained otherwise by storing it in a global variable. Adding `pure` can express this in many cases, but is, of course, not always possible.As you point out, 'ref' is designed for this.Another workaround is passing the parameters as `ref`, but this would introduce an additional indirection and has different semantics (e.g. when the lengths of the slices are modified). 2) `scope ref` return values cannot be stored. scope ref int foo(); void bar(scope ref int a); foo().bar(); // allowed scope tmp = foo(); // not allowed tmp.bar();RightAnother example: struct Container(T) { scope ref T opIndex(size_t index); } void bar(scope ref int a); Container c; bar(c[42]); // ok scope ref tmp = c[42]; // nope Both cases should be fine theoretically; the "real" owner lives longer than `tmp`. Unfortunately the compiler doesn't know about this.Right, though the compiler can optimize to produce the equivalent.Both restrictions 1) and 2) are because there are no explicit lifetime/owner designations (the scope!identifier thingy in my proposal). 3) `scope` cannot be used for value types. I can think of a few use cases for scoped value types (RC and file descriptors), but they might only be marginal.I suspect that one can encapsulate such values in a struct where access to them is strictly controlled.4) No overloading on `scope`. This is at least partially a consequence of `scope` inference. I think overloading can be made to work in the presence of inference, but I haven't thought it through.Right. Different overloads can have different semantic implementations, so what should inference do? I also suspect it is bad style to overload on 'scope'.5) `scope` is a storage class. Manu complained about `ref` being a storage class. If I understand him right, one reason is that we have a large toolkit for dealing with type modifiers, but almost nothing for storage classes. I have to agree with him there. But I haven't understood his point fully, maybe he himself can post more about his problems with this?I didn't fully understand Manu's issue, but it was about 'ref' not being inferred by template type deduction. I didn't understand why 'auto ref' did not work for him. I got the impression that he was trying to program in D the same way he'd do things in C++, and that's where the trouble came in.
Dec 06 2014
On 2014-12-06 10:50, Manu via Digitalmars-d wrote:I've been over it so many times.I suggest you take the time and write down how your vision of "ref" looks like and the issue with the current implementation. A blog post, a DIP or similar. Then you can easily refer to that in cases like this. Then you don't have to repeat yourself so many times. That's what I did when there was a lot of talk about AST macros. I was tired of constantly repeating myself so I created a DIP. It has already saved me more time than it took to write the actual DIP. -- /Jacob Carlborg
Dec 06 2014
On Saturday, 6 December 2014 at 11:06:16 UTC, Jacob Carlborg wrote:On 2014-12-06 10:50, Manu via Digitalmars-d wrote:Manu Seconded. Please create even short one. I coulnd'd find any example of use case you are referring to (as you said I don't use D"ref" so often) . I plan to apply D for embedded systems, so full control is a must. But so far Water still has the most accurate taste according to my experience. BTW. I consider game devs to be the most underpaid programmers, so your perspective is very precious to me. Cheers PiotrekI've been over it so many times.I suggest you take the time and write down how your vision of "ref" looks like and the issue with the current implementation. A blog post, a DIP or similar. Then you can easily refer to that in cases like this. Then you don't have to repeat yourself so many times. That's what I did when there was a lot of talk about AST macros. I was tired of constantly repeating myself so I created a DIP. It has already saved me more time than it took to write the actual DIP.
Dec 06 2014
On Saturday, 6 December 2014 at 23:59:01 UTC, Piotrek wrote:On Saturday, 6 December 2014 at 11:06:16 UTC, Jacob Carlborg wrote:I'd like to not polute this thread with the ref topic. Long story short: - it is hard to know if something is ref, making it hard to metaprogram. - you sometime want to switch ref on and off (for instance, you may use ref to avoid copies, which is not worthwhile for small values) which is complex. - auto ref do not cut it. That is the extra short version, please start a thread on the subject. Please, please, this one is complicated enough.On 2014-12-06 10:50, Manu via Digitalmars-d wrote:Manu Seconded. Please create even short one. I coulnd'd find any example of use case you are referring to (as you said I don't use D"ref" so often) . I plan to apply D for embedded systems, so full control is a must. But so far Water still has the most accurate taste according to my experience. BTW. I consider game devs to be the most underpaid programmers, so your perspective is very precious to me. Cheers PiotrekI've been over it so many times.I suggest you take the time and write down how your vision of "ref" looks like and the issue with the current implementation. A blog post, a DIP or similar. Then you can easily refer to that in cases like this. Then you don't have to repeat yourself so many times. That's what I did when there was a lot of talk about AST macros. I was tired of constantly repeating myself so I created a DIP. It has already saved me more time than it took to write the actual DIP.
Dec 08 2014
On Saturday, 6 December 2014 at 09:51:15 UTC, Manu via Digitalmars-d wrote:I've been over it so many times. I'll start over if there is actually some possibility I can convince you? Is there? I've lost faith that I am able to have any meaningful impact on issues that matter to me, and I'm fairly sure at this point that my compounded resentment and frustration actually discredit my cause, and certainly, my quality of debate. I'm passionate about these things, but I'm also extremely frustrated. To date, whenever I have engaged in a topic that I *really* care about, it goes the other direction. To participate has proven to be something of a (very time consuming) act of masochism.That's very sad to hear. I think you have brought up some very important points, especially from a practical point of view.I __absolutely objected__ to 'auro ref' when it appeared. I argued that it was a massive mistake, and since it's introduction and experience with it in the wild, I am more confident in that conviction than ever. As a programmer, I expect control over whether code is a template, or not. 'ref' doesn't have anything to do with templates. Confusing these 2 concepts was such a big mistake. auto ref makes a template out of something that shouldn't be a template. It's a particularly crude hack to address a prior mistake. ref should have been fixed at that time, not compounded with layers of even more weird and special-case/non-uniform behaviours above it. There has been a couple of instances where the situation has been appropriate that I've tried to make use of auto ref, but in each case, the semantics have never been what I want. auto ref presumes to decide for you when something should be a ref or not. It prescribes a bunch of rules on how that decision is made, but it doesn't know what I'm doing, and it gets it wrong. In my experience, auto ref has proven to be, at best, completely useless. But in some cases I've found where I bump into it in 3rd party code, it's been a nuisance, requiring me to wrap it away.I tend to agree with this. Here's an example of the dangers: https://github.com/deadalnix/libd/pull/7 In this case, `auto ref` accepted a class by reference, because it chooses ref-ness based on whether you pass an lvalue or an rvalue. This is not very helpful. I agree that `ref` isn't something that should ever be inferred, because it affects the semantics of the function. Either the function requires `ref` semantics, or not. It doesn't depend on how it is called.
Dec 06 2014
Marc Schütz:I agree that `ref` isn't something that should ever be inferred, because it affects the semantics of the function. Either the function requires `ref` semantics, or not. It doesn't depend on how it is called.(Perhaps this was already said) I think Ada used to have something like "auto ref" and later it was removed from the language. Bye, bearophile
Dec 06 2014
On 12/6/2014 1:50 AM, Manu via Digitalmars-d wrote:I apologize for misunderstanding you.I didn't fully understand Manu's issue, but it was about 'ref' not being inferred by template type deduction. I didn't understand why 'auto ref' did not work for him. I got the impression that he was trying to program in D the same way he'd do things in C++, and that's where the trouble came in.NO!! I barely program C++ at all! I basically write C code with 'enum' and 'class'. NEVER 'template', and very rarely 'virtual'. Your impression is dead wrong.It's exactly as Marc says, we have the best tools for dealing with types of any language I know, and practically none for dealing with 'storage class'. In my use of meta in D, 'ref' is the single greatest cause of complexity, code bloat, duplication, and text mixins. I've been banging on about this for years! I've been over it so many times. I'll start over if there is actually some possibility I can convince you? Is there?I know there's no easy way to derive a storage class from an expression. The difficulty in my understanding is why this is a great cause of problems for you in particular (and by implication not for others). There's something about the way you write code that's different.I've lost faith that I am able to have any meaningful impact on issues that matter to me, and I'm fairly sure at this point that my compounded resentment and frustration actually discredit my cause, and certainly, my quality of debate.This is incorrect, you had enormous influence over the vector type in D! And I wish you had more.There has been a couple of instances where the situation has been appropriate that I've tried to make use of auto ref, but in each case, the semantics have never been what I want.I don't really know what you want. Well, perhaps a better statement is 'why', not what.ref is a bad design. C++'s design isn't fantastic, and I appreciate that D made effort to improve on it, but we need to recognise when the experiment was a failure. D's design is terrible; it's basically orthogonal to the rest of the language. It's created way more complicated edge cases for me than C++ references ever have. Anyone who says otherwise obviously hasn't really used it much! Don't double down on that mistake with scope.Why does your code need to care so much about to ref or not to ref? That's the central point here, I think.My intended reply to this post was to ask you to justify making it a storage class, and why the design fails as a type constructor? Can we explore that direction to it's point of failure? As a storage class, it runs the risk of doubling out existing bloat caused by ref. As a type constructor, I see no disadvantages. It even addresses some of the awkward problems right at the face of storage classes, like how/where the attributes actually apply. Type constructors use parens; ie, const(T), and scope(T) would make that matter a lot more clear. Apart from the storage class issue, it looks okay, but it gives me the feeling that it kinda stops short. Marc's proposal addressed more issues. I feel this proposal will result in more edge cases than Marc's proposal. The major edge case that I imagine is that since scope return values can't be assigned to scope local's, that will result in some awkward meta, requiring yet more special cases. I think Mark's proposal may be a lot more relaxed in that way.The disadvantages of making it a type qualifier are: 1. far more complexity. Type constructors interact with everything, often in unanticipated ways. We spent *years* working out issues with the 'const' type qualifier, and are still doing so. Kenji just fixed another one. 2. we are never going to get users to use 'scope' qualifiers pervasively. It's been a long struggle to get 'const' used. 3. we added 'inout' as a type qualifier to avoid code duplication engendered by 'const'. It hurts my brain to even think about how that might interact with 'scope' qualifiers. Yes, I agree unequivocably, that 'scope' as a type qualifier is more expressive and more powerful than as a storage class. Multiple inheritance is also more expressive and more powerful than single inheritance. But many times, more power perhaps isn't better than redoing the program design to use something simpler and less complex.
Dec 06 2014
On 7 December 2014 at 08:15, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 12/6/2014 1:50 AM, Manu via Digitalmars-d wrote:Perhaps it's the tasks I typically perform with meta? I'm a fairly conservative user of templates, but one place where they shine, and I'm always very tempted to use them, is the task of serialisation, and cross-language bindings. Those tasks typically involve mountains of boilerplate, and D is the only language expressive enough to start to really automate that mechanical mess. subtly different tasks, but share a lot in common, and the major characteristic which reveals my problems with things like ref, and auto ref, is that these things aren't open to interpretation. They're not cases where the compiler can make decisions for you (ie, auto ref fails), and the ABI + API are strict (ie, ref must match correctly). There's a lot more more to it, but perhaps I'm among few who have had such extensive need to engage in these tasks?I apologize for misunderstanding you.I didn't fully understand Manu's issue, but it was about 'ref' not being inferred by template type deduction. I didn't understand why 'auto ref' did not work for him. I got the impression that he was trying to program in D the same way he'd do things in C++, and that's where the trouble came in.NO!! I barely program C++ at all! I basically write C code with 'enum' and 'class'. NEVER 'template', and very rarely 'virtual'. Your impression is dead wrong.It's exactly as Marc says, we have the best tools for dealing with types of any language I know, and practically none for dealing with 'storage class'. In my use of meta in D, 'ref' is the single greatest cause of complexity, code bloat, duplication, and text mixins. I've been banging on about this for years! I've been over it so many times. I'll start over if there is actually some possibility I can convince you? Is there?I know there's no easy way to derive a storage class from an expression. The difficulty in my understanding is why this is a great cause of problems for you in particular (and by implication not for others). There's something about the way you write code that's different.I appreciate that. That was uncontroversial though; I didn't need to spend months or years trying to justify my claims on that issue. I feel like that was a known item that was just somewhere slightly down the list, and I was able to bring it forward. I was never in a position where I had to argue against Andrei to sell that one. I have std.simd sitting here, and I really want to finish it, but I still don't have the tools to do so. I need, at least, forceinline to complete it, but that one *is* controversial - we've talked about this for years. GDC and LDC both have a forceinline, so I could theoretically support those compilers, but then I can't practically make use of them without some sort of attribute aliasing system, otherwise I need to triplicate the code for each compiler, just to insert a different (compiler specific) forceinline attribute name. It'd be really great if we agreed on just one. There is also a practical problem with GCC (perhaps it's an incompatbility with my std.simd design), where I need to change the sse-level between functions. There is a GCC attribute to do this ('target'), but it would rely on, at least, a few subtle tweaks. In this case, I need to be able to feed a template argument to a UDA, which doesn't work because UDA declarations seem to be parsed prior to knowledge of functions template args. There may be a further problem with GCC though which I haven't been able to prove yet though. The reason for this is that an important design goal for my work was to be able to semi-automatically generate multiple code paths for different SSE versions, which can be selected at runtime based on available hardware. It's a particularly awkward issue in C/C++, usually requiring you to have multiple modules which are each built with different compile flags, and then do some magic while linking to resolve the runtime selection problem. Anyway, I have been able to use SIMD directly in my own software with the support we have, but I haven't been able to complete the library that I want to produce. I need a little more language support. I've reached out a few times, but there hasn't been too much interest in the past. I really do wanna finish it one of these days though!I've lost faith that I am able to have any meaningful impact on issues that matter to me, and I'm fairly sure at this point that my compounded resentment and frustration actually discredit my cause, and certainly, my quality of debate.This is incorrect, you had enormous influence over the vector type in D! And I wish you had more.Because a function is a function. It's a fundamental and ultimately *simple* element of a language, with simple and reliable in behaviour; you write a function, compiler emits some code with a matching symbol name. There is perhaps nothing more simple or fundamental to the language. A template is not a function. It may *generate* a function, but now we've added many extra details which require careful consideration and handling, like where the code is, or isn't, emitted? How many permutations are there? What about static/dynamic libraries? Calling with various, or arbitrary types, always required careful consideration, more so than an explicit type. Templates are a powerful (often dangerous) **tool**, which should only be applied deliberately and very carefully. Obviously auto-ref gives hard answers to some of those questions, but it is still a template, and that instantly makes it much more complex. Making a function that has nothing to do with templates into a template is not what I want... it couldn't be further from what I ever wanted! The ABI is different; there are 2 functions now (or zero, until one is called). Thich means taking the address of a function with auto-ref is a complex issue. ref-ness isn't a decision I ever want the compiler to make for me. I either want ref, or I don't, and I will state that to the compiler explicitly. This is especially true when interacting with other languages; while D remains in relative infancy, I think that is an overwhelmingly common situation when a project is modestly large. In the situation where templates are involved, it would be nice to be able to make that explicit statement that some type is ref or not at the point of template instantiation, and the resolution should work according to the well-defined rules that we are all familiar with. Within a complex template, if logic should be performed, we have powerful tools to do that already; nobody ever complains about Unqual!, or PointerTarget!... these things are very easy to read and understand. ref should fit right in there with the others, is(x == ref), UnRef!T, etc. Instead, it's practically orthogonal to the language. Wrangling anything involving storage classes is really, really tedious, and almost always results in extensive code duplication, or text mixins when duplication just get too much. This issue really surprised me at the time, because the whole thing was in response to a topic that I consistently raised and pushed. Who was the customer welcoming that solution? I could never work it out; there was nobody else that seemed particularly invested in the problem, except the usual suite of language enthusiasts that took it as an interesting intellectual problem to discuss. The person who cared about that issue the most (afaict) was completely unsatisfied with the solution. I said at the time that I would have preferred that no action was taken, than to compound, and further set in stone, an issue that I was already extremely critical of. Don't double down on this mistake with scope!There has been a couple of instances where the situation has been appropriate that I've tried to make use of auto ref, but in each case, the semantics have never been what I want.I don't really know what you want. Well, perhaps a better statement is 'why', not what.That same logic could ask why I want to have 'short', or 'int' in some places, but not others. There's no absolute answer. As a software engineer, and particularly, a native software engineer, it's a fundamental part of the type system (sorry, not part of the type system!) that I must have control over. Why offer 'ref' if you don't intend people to use it? I put to you, why do you hate 'ref' so much? Remove it from the language if people aren't meant to use it. ref is just a pointer with some semantics removed (pointer re-assignment, indexing). It's use facilitates some forms of generic code which would fail otherwise, and also reduces some possibilities for end-user misuse or mistakes when using pointers (ie, user may intend an assignment, but unsuspectingly re-assign a pointer; result looks the same, but exposes bug somewhere else). It's also used as an optimisation where I require control over the ref-ness of things, but don't want to retrofit the code with a sea of '*' and '&' operators. It's also used when interfacing C++, where it appears in API's extensively. I'm sure there are many more common and useful cases where ref is a good tool.ref is a bad design. C++'s design isn't fantastic, and I appreciate that D made effort to improve on it, but we need to recognise when the experiment was a failure. D's design is terrible; it's basically orthogonal to the rest of the language. It's created way more complicated edge cases for me than C++ references ever have. Anyone who says otherwise obviously hasn't really used it much! Don't double down on that mistake with scope.Why does your code need to care so much about to ref or not to ref? That's the central point here, I think.What you describe is is the opposite of 'complexity'. Uniform behaviour is what people expect from a language. I understand complexity may arise as a result of interaction with other features, but that's a worthwhile issue to explore if you ask me. That's complexity that can actually be addressed, rather than pushed to the side. Edge cases which are orthogonal to the rest of the language (ie, ref), are a terrible idea. You don't buffer against complexity by creating a whole new class of complexity, which we have no effective tools to manage or mitigate.My intended reply to this post was to ask you to justify making it a storage class, and why the design fails as a type constructor? Can we explore that direction to it's point of failure? As a storage class, it runs the risk of doubling out existing bloat caused by ref. As a type constructor, I see no disadvantages. It even addresses some of the awkward problems right at the face of storage classes, like how/where the attributes actually apply. Type constructors use parens; ie, const(T), and scope(T) would make that matter a lot more clear. Apart from the storage class issue, it looks okay, but it gives me the feeling that it kinda stops short. Marc's proposal addressed more issues. I feel this proposal will result in more edge cases than Marc's proposal. The major edge case that I imagine is that since scope return values can't be assigned to scope local's, that will result in some awkward meta, requiring yet more special cases. I think Mark's proposal may be a lot more relaxed in that way.The disadvantages of making it a type qualifier are: 1. far more complexity. Type constructors interact with everything, often in unanticipated ways. We spent *years* working out issues with the 'const' type qualifier, and are still doing so. Kenji just fixed another one.2. we are never going to get users to use 'scope' qualifiers pervasively. It's been a long struggle to get 'const' used.I don't think scope will be as important as const. I think we should *try it*, to experimentally see how it plays out. That said, scope should be able to be inferred quite effectively in many most cases. Again, I think we will only be able to measure the effectiveness of this when we try it.3. we added 'inout' as a type qualifier to avoid code duplication engendered by 'const'. It hurts my brain to even think about how that might interact with 'scope' qualifiers.inout was an interesting idea (with a terrible name!). I'm still not sure if I think it was a good idea or not, but I have found it very useful. What's the issue? I don't quite see how inout and scope overlap (no differently than const or immutable?).Yes, I agree unequivocably, that 'scope' as a type qualifier is more expressive and more powerful than as a storage class. Multiple inheritance is also more expressive and more powerful than single inheritance. But many times, more power perhaps isn't better than redoing the program design to use something simpler and less complex.I don't think that's a reasonable comparison. Multiple inheritance is (I think quite well agreed) a wildly unpopular and super-complex disaster. Comparing scope-as-a-storage-class or scope-as-a-type-constructor is nothing like comparing multiple inheritence and interfaces... interfaces aren't orthogonal to the language for a start! And they're well understood, and precedented in other languages. Don't introduce red herrings which incite unrelated, yet strong emotional response. My argument is that scope as storage class is MORE COMPLEX in that it is orthogonal to the language, than scope as type constructor. I think users will also find it extremely unintuitive, when they realise that all the usual tools for interacting with types in the language are unavailable in this special case. I think scope will also prove to be more popular than ref, so, while you don't hear so much about how much of a disaster ref is, you'll start to hear all the exact same problems arise when people are trying to use scope, because it's a far more interesting type qualifier (and should probably be the default). ...I can't wait for 'auto scope'! ;)
Dec 06 2014
On 12/6/2014 4:49 PM, Manu via Digitalmars-d wrote:On 7 December 2014 at 08:15, Walter Bright via Digitalmars-d There's a lot more more to it, but perhaps I'm among few who have had such extensive need to engage in these tasks?I don't know, but it's a mystery to me what you're doing that there's no reasonable alternative, or why this is such an extensive issue for you.UDA was controversial, and was one of your initiatives.This is incorrect, you had enormous influence over the vector type in D! And I wish you had more.I appreciate that. That was uncontroversial though; I didn't need to spend months or years trying to justify my claims on that issue. I feel like that was a known item that was just somewhere slightly down the list, and I was able to bring it forward. I was never in a position where I had to argue against Andrei to sell that one.I have std.simd sitting here, and I really want to finish it, but I still don't have the tools to do so. I need, at least, forceinline to complete it, but that one *is* controversial - we've talked about this for years.I proposed a DIP to fix that, but it could not get reasonable consensus. http://wiki.dlang.org/DIP56 You did, after all, convince me that we need an "always inline" and a "never inline" method.There is also a practical problem with GCC (perhaps it's an incompatbility with my std.simd design), where I need to change the sse-level between functions. There is a GCC attribute to do this ('target'), but it would rely on, at least, a few subtle tweaks. In this case, I need to be able to feed a template argument to a UDA, which doesn't work because UDA declarations seem to be parsed prior to knowledge of functions template args. There may be a further problem with GCC though which I haven't been able to prove yet though.I don't know why this needs to be a deduced template argument.The reason for this is that an important design goal for my work was to be able to semi-automatically generate multiple code paths for different SSE versions, which can be selected at runtime based on available hardware.No argument there.I need a little more language support. I've reached out a few times, but there hasn't been too much interest in the past. I really do wanna finish it one of these days though!I want it done, too :-)I view a template function as a function that takes both compile-time and run-time arguments, not as a function generator. (The latter is an implementation artifact.)I don't really know what you want. Well, perhaps a better statement is 'why', not what.Because a function is a function. It's a fundamental and ultimately *simple* element of a language, with simple and reliable in behaviour; you write a function, compiler emits some code with a matching symbol name. There is perhaps nothing more simple or fundamental to the language. A template is not a function. It may *generate* a function, but now we've added many extra details which require careful consideration and handling, like where the code is, or isn't, emitted? How many permutations are there? What about static/dynamic libraries? Calling with various, or arbitrary types, always required careful consideration, more so than an explicit type. Templates are a powerful (often dangerous) **tool**, which should only be applied deliberately and very carefully. Obviously auto-ref gives hard answers to some of those questions, but it is still a template, and that instantly makes it much more complex.Making a function that has nothing to do with templates into a template is not what I want... it couldn't be further from what I ever wanted!I don't see any problem with: T func()(runtime args ...) { ... } That idiom has found various uses in practice.The ABI is different; there are 2 functions now (or zero, until one is called). Thich means taking the address of a function with auto-ref is a complex issue. ref-ness isn't a decision I ever want the compiler to make for me.But why?I either want ref, or I don't, and I will state that to the compiler explicitly. This is especially true when interacting with other languages; while D remains in relative infancy, I think that is an overwhelmingly common situation when a project is modestly large.The set of other languages that support ref parameters is [C++]. None others I know of that are accessible from D. Is it commonplace in C++ to have these overloads: T foo(S s); T foo(S& s); ? Such a practice would at least raise an eyebrow for me.In the situation where templates are involved, it would be nice to be able to make that explicit statement that some type is ref or not at the point of template instantiation, and the resolution should work according to the well-defined rules that we are all familiar with. Within a complex template, if logic should be performed, we have powerful tools to do that already; nobody ever complains about Unqual!, or PointerTarget!... these things are very easy to read and understand. ref should fit right in there with the others, is(x == ref), UnRef!T, etc.http://dlang.org/traits.html#isRefInstead, it's practically orthogonal to the language. Wrangling anything involving storage classes is really, really tedious, and almost always results in extensive code duplication, or text mixins when duplication just get too much. This issue really surprised me at the time, because the whole thing was in response to a topic that I consistently raised and pushed. Who was the customer welcoming that solution? I could never work it out; there was nobody else that seemed particularly invested in the problem, except the usual suite of language enthusiasts that took it as an interesting intellectual problem to discuss. The person who cared about that issue the most (afaict) was completely unsatisfied with the solution. I said at the time that I would have preferred that no action was taken, than to compound, and further set in stone, an issue that I was already extremely critical of. Don't double down on this mistake with scope!The thing is, I don't understand *why* you want to wrangle storage classes. What is the coding pattern?I can give practical technical reasons why for 'short' or 'int'.Why does your code need to care so much about to ref or not to ref? That's the central point here, I think.That same logic could ask why I want to have 'short', or 'int' in some places, but not others. There's no absolute answer. As a software engineer, and particularly, a native software engineer, it's a fundamental part of the type system (sorry, not part of the type system!) that I must have control over.Why offer 'ref' if you don't intend people to use it? I put to you, why do you hate 'ref' so much? Remove it from the language if people aren't meant to use it.That's the wrong question. I could ask "why do you hate ref as a storage class"?ref is just a pointer with some semantics removed (pointer re-assignment, indexing). It's use facilitates some forms of generic code which would fail otherwise, and also reduces some possibilities for end-user misuse or mistakes when using pointers (ie, user may intend an assignment, but unsuspectingly re-assign a pointer; result looks the same, but exposes bug somewhere else). It's also used as an optimisation where I require control over the ref-ness of things, but don't want to retrofit the code with a sea of '*' and '&' operators. It's also used when interfacing C++, where it appears in API's extensively. I'm sure there are many more common and useful cases where ref is a good tool.And D's ref works for all that.Uniform behavior is a nice ideal, and sounds good, but it never happens in practice with programming languages. We can't even get 'int' to behave uniformly! (Quick: what is -int.min ?) You might also consider the "uniformity" of the ref type in C++. It's awful - it's a special case EVERYWHERE in the C++ type system! It just does not fit as a type qualifier.The disadvantages of making it a type qualifier are: 1. far more complexity. Type constructors interact with everything, often in unanticipated ways. We spent *years* working out issues with the 'const' type qualifier, and are still doing so. Kenji just fixed another one.What you describe is is the opposite of 'complexity'. Uniform behaviour is what people expect from a language. I understand complexity may arise as a result of interaction with other features, but that's a worthwhile issue to explore if you ask me. That's complexity that can actually be addressed, rather than pushed to the side. Edge cases which are orthogonal to the rest of the language (ie, ref), are a terrible idea. You don't buffer against complexity by creating a whole new class of complexity, which we have no effective tools to manage or mitigate.'scope' is so important we may even consider it to be the default.2. we are never going to get users to use 'scope' qualifiers pervasively. It's been a long struggle to get 'const' used.I don't think scope will be as important as const. I think we should *try it*, to experimentally see how it plays out. That said, scope should be able to be inferred quite effectively in many most cases. Again, I think we will only be able to measure the effectiveness of this when we try it.Every type will have scope and nonscope variants, combinatorialy with the other type qualifiers.3. we added 'inout' as a type qualifier to avoid code duplication engendered by 'const'. It hurts my brain to even think about how that might interact with 'scope' qualifiers.inout was an interesting idea (with a terrible name!). I'm still not sure if I think it was a good idea or not, but I have found it very useful. What's the issue? I don't quite see how inout and scope overlap (no differently than const or immutable?).Nobody has successfully introduced a type qualifier like scope.Yes, I agree unequivocably, that 'scope' as a type qualifier is more expressive and more powerful than as a storage class. Multiple inheritance is also more expressive and more powerful than single inheritance. But many times, more power perhaps isn't better than redoing the program design to use something simpler and less complex.I don't think that's a reasonable comparison. Multiple inheritance is (I think quite well agreed) a wildly unpopular and super-complex disaster. Comparing scope-as-a-storage-class or scope-as-a-type-constructor is nothing like comparing multiple inheritence and interfaces... interfaces aren't orthogonal to the language for a start! And they're well understood, and precedented in other languages. Don't introduce red herrings which incite unrelated, yet strong emotional response.My argument is that scope as storage class is MORE COMPLEX in that it is orthogonal to the language, than scope as type constructor. I think users will also find it extremely unintuitive, when they realise that all the usual tools for interacting with types in the language are unavailable in this special case. I think scope will also prove to be more popular than ref, so, while you don't hear so much about how much of a disaster ref is, you'll start to hear all the exact same problems arise when people are trying to use scope, because it's a far more interesting type qualifier (and should probably be the default). ...I can't wait for 'auto scope'! ;)I guess we'll see! I agree that this design is untried, and we don't really know how it will work out.
Dec 06 2014
"Walter Bright" wrote in message news:m60oa4$kd5$1 digitalmars.com...From the DIP:I have std.simd sitting here, and I really want to finish it, but I still don't have the tools to do so. I need, at least, forceinline to complete it, but that one *is* controversial - we've talked about this for years.I proposed a DIP to fix that, but it could not get reasonable consensus. http://wiki.dlang.org/DIP56 You did, after all, convince me that we need an "always inline" and a "never inline" method.If a pragma specifies always inline, whether or not the target function(s) are actually inlined is implementation defined, although the implementation will be expected to inline it if practical. Implementations will likely vary in their ability to inline.I expect a proposal in which 'always' means "inline or give an error" would be much better received.The thing is, I don't understand *why* you want to wrangle storage classes. What is the coding pattern?That's the wrong question. I could ask "why do you hate ref as a storage class"?I suspect the answer is that sometimes it is useful to ensure some parameters are passed by reference, not by value. With ref as a type, this would be trivial: template MyParamType(T) { static if (someCriteria!T) alias MyParamType = ref(T); else alias MyParamType = T; } void myFunction(T, U, V)(const MyParamType!T, const MyParamType!U, const MyParamType!V) { ... }
Dec 06 2014
On Sunday, 7 December 2014 at 05:24:20 UTC, Walter Bright wrote:I don't think UDA is controversial, but the way it was done certainly is controverted.I appreciate that. That was uncontroversial though; I didn't need to spend months or years trying to justify my claims on that issue. I feel like that was a known item that was just somewhere slightly down the list, and I was able to bring it forward. I was never in a position where I had to argue against Andrei to sell that one.UDA was controversial, and was one of your initiatives.
Dec 08 2014
On 2014-12-07 01:49, Manu via Digitalmars-d wrote:I have std.simd sitting here, and I really want to finish it, but I still don't have the tools to do so. I need, at least, forceinline to complete it, but that one *is* controversial - we've talked about this for years. GDC and LDC both have a forceinline, so I could theoretically support those compilers, but then I can't practically make use of them without some sort of attribute aliasing system, otherwise I need to triplicate the code for each compiler, just to insert a different (compiler specific) forceinline attribute name. It'd be really great if we agreed on just one.I don't know about LDC but at least GDC allows you to use UDA's instead of a pragma. Then you can create a dummy attribute for DMD (and LDC): version (GNU) import gcc.attribute else { struct attribute { string attr; } } attribute("forceinline") void foo (); -- /Jacob Carlborg
Dec 07 2014
On 7 Dec 2014 10:40, "Jacob Carlborg via Digitalmars-d" < digitalmars-d puremagic.com> wrote:On 2014-12-07 01:49, Manu via Digitalmars-d wrote:of a pragma. Then you can create a dummy attribute for DMD (and LDC):I have std.simd sitting here, and I really want to finish it, but I still don't have the tools to do so. I need, at least, forceinline to complete it, but that one *is* controversial - we've talked about this for years. GDC and LDC both have a forceinline, so I could theoretically support those compilers, but then I can't practically make use of them without some sort of attribute aliasing system, otherwise I need to triplicate the code for each compiler, just to insert a different (compiler specific) forceinline attribute name. It'd be really great if we agreed on just one.I don't know about LDC but at least GDC allows you to use UDA's insteadversion (GNU) import gcc.attribute else { struct attribute { string attr; } } attribute("forceinline") void foo ();You can add shorthand aliases for them too. :) forceinline void foo ();
Dec 07 2014
On 2014-12-07 11:50, Iain Buclaw via Digitalmars-d wrote:You can add shorthand aliases for them too. :) forceinline void foo ();Good point. -- /Jacob Carlborg
Dec 07 2014
On 12/6/14 4:49 PM, Manu via Digitalmars-d wrote:I need, at least, forceinline to complete it, but that one*is* controversial - we've talked about this for years.I'm still 883 messages behind so here's a drive-by comment - it's time to revisit this, I think the need has become a lot clearer. -- Andrei
Dec 20 2014
On 12/6/14 4:49 PM, Manu via Digitalmars-d wrote:In the situation where templates are involved, it would be nice to be able to make that explicit statement that some type is ref or not at the point of template instantiation, and the resolution should work according to the well-defined rules that we are all familiar with.Another drive-by comment: I understand the motivation for this and the difficulties involved. There needs to be a clear understanding that adding new type qualifiers is extremely intrusive and expensive. Because of that, I think we should best address binding generation via a set of tactical tools i.e. standard library artifacts that do all that mixin business in an encapsulated and reusable manner. (As an aside forcing a template instantiation to decide ref vs. no ref should be easy but currently can't be done, i.e. this code should work but currently doesn't: T fun(T)(ref T x) { return x + 1; } void main(string[] group) { int function(int) f1 = &fun!int; int function(ref int) f2 = &fun!int; } ) Andrei
Dec 20 2014
On Friday, 5 December 2014 at 23:58:41 UTC, Walter Bright wrote:On 12/5/2014 8:48 AM, "Marc Schütz" <schuetzm gmx.net>" wrote:I wouldn't call it "designed", but "repurposed"...1) Escape detection is limited to `ref`. T* evil; ref T func(scope ref T t, ref T u) safe { return t; // Error: escaping scope ref t return u; // ok evil = &u; // Error: escaping reference } vs. T[] evil; T[] func(scope T[] t, T[] u) safe { return t; // Error: cannot return scope return u; // ok evil = u; // !!! not goodright, although: evil = t; // Error: not allowed} As can be seen, `ref T u` is protected from escaping (apart from returning it), while `T[] u` in the second example is not. There's no general way to express that `u` can only be returned from the function, but will not be retained otherwise by storing it in a global variable. Adding `pure` can express this in many cases, but is, of course, not always possible.As you point out, 'ref' is designed for this.??? This is a problem on the semantic level, unrelated to optimization: // contrived example to illustrated the point Container c; scope ref x = c[42]; // not scope ref y = c[44]; // ... scope ref z = c[13]; // allowed foo(x, y, z, x+y, y+z, z+x, x+y+z); // workaround, but error-prone and has different semantics // (opIndex may have side effects, called multiple times) foo(c[42], c[44], c[13], c[42]+c[44], c[44]+c[13], c[13]+c[42], c[42]+c[44]+c[13]); // another workaround, same semantics, but ugly and unreadable (scope ref int x, scope ref int y, scope ref int z) { foo(x, y, z, x+y, y+z, z+x, x+y+z); }(c[42], c[44], c[13]);struct Container(T) { scope ref T opIndex(size_t index); } void bar(scope ref int a); Container c; bar(c[42]); // ok scope ref tmp = c[42]; // nope Both cases should be fine theoretically; the "real" owner lives longer than `tmp`. Unfortunately the compiler doesn't know about this.Right, though the compiler can optimize to produce the equivalent.They can, but again at the cost of an indirection (ref).3) `scope` cannot be used for value types. I can think of a few use cases for scoped value types (RC and file descriptors), but they might only be marginal.I suspect that one can encapsulate such values in a struct where access to them is strictly controlled.It only makes sense with scope value types (see the RC example). For references, I don't know any useful applications.4) No overloading on `scope`. This is at least partially a consequence of `scope` inference. I think overloading can be made to work in the presence of inference, but I haven't thought it through.Right. Different overloads can have different semantic implementations, so what should inference do? I also suspect it is bad style to overload on 'scope'.Ok, so let's drop bar2() and bar4(). scope ref int foo(); scope ref int bar1(ref int a) { return a; } ref int bar3(ref int a) { return a; } ref int baz_noscope(/*scope*/ ref int a); foo().bar1().baz_noscope(); foo().bar3().baz_noscope(); And now? In particular, will the return value of `bar3` be treated as if it were `scope ref`?6) There seem to be problems with chaining. scope ref int foo(); scope ref int bar1(ref int a) { return a; } scope ref int bar2(scope ref int a) { return a; } ref int bar3(ref int a) { return a; } ref int bar4(scope ref int a) { return a; } void baz(scope ref int a); Which of the following calls would work? foo().bar1().baz();yesfoo().bar2().baz();no - cannot return scope ref parameterfoo().bar3().baz();yesfoo().bar4().baz();no, cannot return scope ref parameterI'm not sure I understand this fully yet, but it could be that none of them work...Well, you're half right :-)
Dec 06 2014
On 12/6/2014 5:30 AM, "Marc Schütz" <schuetzm gmx.net>" wrote:On Friday, 5 December 2014 at 23:58:41 UTC, Walter Bright wrote:Perhaps, but the original reason to even have 'ref' was so it could be a restricted pointer type that could be passed down a call hierarchy, but not up.As you point out, 'ref' is designed for this.I wouldn't call it "designed", but "repurposed"...You are correct, and it remains to be seen if these occur enough to be a problem or not. The workarounds do exist, though. Another workaround: scope ref tmp = c[42]; becomes: auto scope ref tmp() { return c[42]; }??? This is a problem on the semantic level, unrelated to optimization: // contrived example to illustrated the point Container c; scope ref x = c[42]; // not scope ref y = c[44]; // ... scope ref z = c[13]; // allowed foo(x, y, z, x+y, y+z, z+x, x+y+z); // workaround, but error-prone and has different semantics // (opIndex may have side effects, called multiple times) foo(c[42], c[44], c[13], c[42]+c[44], c[44]+c[13], c[13]+c[42], c[42]+c[44]+c[13]); // another workaround, same semantics, but ugly and unreadable (scope ref int x, scope ref int y, scope ref int z) { foo(x, y, z, x+y, y+z, z+x, x+y+z); }(c[42], c[44], c[13]);struct Container(T) { scope ref T opIndex(size_t index); } void bar(scope ref int a); Container c; bar(c[42]); // ok scope ref tmp = c[42]; // nope Both cases should be fine theoretically; the "real" owner lives longer than `tmp`. Unfortunately the compiler doesn't know about this.Right, though the compiler can optimize to produce the equivalent.Ok, so let's drop bar2() and bar4(). scope ref int foo(); scope ref int bar1(ref int a) { return a; } ref int bar3(ref int a) { return a; } ref int baz_noscope(/*scope*/ ref int a); foo().bar1().baz_noscope(); foo().bar3().baz_noscope(); And now? In particular, will the return value of `bar3` be treated as if it were `scope ref`?Yes.
Dec 06 2014
On Friday, 5 December 2014 at 23:58:41 UTC, Walter Bright wrote:On 12/5/2014 8:48 AM, "Marc Schütz" <schuetzm gmx.net>" wrote:I understand that scope will not allow the contents of the variable to escape the lifetime of a declaration. But can you explain why bar1() works, but bar2() doesn't? Isn't the body of bar2() in the line `foo().bar2();` part of the declaration? Besides, what does it mean to return a `scope ref int`? Does it mean that the content of the variable that is returned is not allowed to escape the scope of the calling site? Huh? It seemed so easy when you gave the example. On Friday, 5 December 2014 at 20:55:55 UTC, Walter Bright wrote:scope ref int foo(); scope ref int bar1(ref int a) { return a; } scope ref int bar2(scope ref int a) { return a; } ref int bar3(ref int a) { return a; } ref int bar4(scope ref int a) { return a; } void baz(scope ref int a); Which of the following calls would work? foo().bar1().baz();yesfoo().bar2().baz();no - cannot return scope ref parameterfoo().bar3().baz();yesfoo().bar4().baz();no, cannot return scope ref parameterIt means that this code will be safe: void foo(scope int* p); p = malloc(n); foo(p); free(p); The rest is all the nuts and bolts of making that work.
Dec 07 2014
On 12/7/2014 2:46 AM, Sebastiaan Koppe wrote:On Friday, 5 December 2014 at 23:58:41 UTC, Walter Bright wrote:A 'scope ref' parameter may not be returned as a 'ref' or a 'scope ref'.On 12/5/2014 8:48 AM, "Marc Schütz" <schuetzm gmx.net>" wrote:I understand that scope will not allow the contents of the variable to escape the lifetime of a declaration. But can you explain why bar1() works, but bar2() doesn't?scope ref int foo(); scope ref int bar1(ref int a) { return a; } scope ref int bar2(scope ref int a) { return a; } ref int bar3(ref int a) { return a; } ref int bar4(scope ref int a) { return a; } void baz(scope ref int a); Which of the following calls would work? foo().bar1().baz();yesfoo().bar2().baz();no - cannot return scope ref parameterfoo().bar3().baz();yesfoo().bar4().baz();no, cannot return scope ref parameterIsn't the body of bar2() in the line `foo().bar2();` part of the declaration? Besides, what does it mean to return a `scope ref int`? Does it mean that the content of the variable that is returned is not allowed to escape the scope of the calling site? Huh?It means the reference itself (the pointer) does not escape.
Dec 08 2014
On Monday, 8 December 2014 at 21:16:36 UTC, Walter Bright wrote:A 'scope ref' parameter may not be returned as a 'ref' or a 'scope ref'.It can safely be returned if you consider its lifetime as the intersection of the lifetime of the function's parameter.
Dec 08 2014
On 12/8/2014 3:21 PM, deadalnix wrote:On Monday, 8 December 2014 at 21:16:36 UTC, Walter Bright wrote:The only purpose to a 'scope ref' parameter is to say it isn't being returned. 'ref' itself does not escape in any way other than by returning.A 'scope ref' parameter may not be returned as a 'ref' or a 'scope ref'.It can safely be returned if you consider its lifetime as the intersection of the lifetime of the function's parameter.
Dec 09 2014
On Wednesday, 10 December 2014 at 05:23:29 UTC, Walter Bright wrote:On 12/8/2014 3:21 PM, deadalnix wrote:That is a completely useless feature. Also, you want to have scope return for container like thing.On Monday, 8 December 2014 at 21:16:36 UTC, Walter Bright wrote:The only purpose to a 'scope ref' parameter is to say it isn't being returned. 'ref' itself does not escape in any way other than by returning.A 'scope ref' parameter may not be returned as a 'ref' or a 'scope ref'.It can safely be returned if you consider its lifetime as the intersection of the lifetime of the function's parameter.
Dec 09 2014
On 12/9/2014 10:43 PM, deadalnix wrote:On Wednesday, 10 December 2014 at 05:23:29 UTC, Walter Bright wrote:I disagree. It's critical for chaining one function to the next.On 12/8/2014 3:21 PM, deadalnix wrote:That is a completely useless feature. Also, you want to have scope return for container like thing.On Monday, 8 December 2014 at 21:16:36 UTC, Walter Bright wrote:The only purpose to a 'scope ref' parameter is to say it isn't being returned. 'ref' itself does not escape in any way other than by returning.A 'scope ref' parameter may not be returned as a 'ref' or a 'scope ref'.It can safely be returned if you consider its lifetime as the intersection of the lifetime of the function's parameter.
Dec 10 2014
On Thursday, 11 December 2014 at 03:27:08 UTC, Walter Bright wrote:I disagree. It's critical for chaining one function to the next.I one can't return, one can't chain.
Dec 10 2014
On 12/10/2014 8:56 PM, deadalnix wrote:On Thursday, 11 December 2014 at 03:27:08 UTC, Walter Bright wrote:I guess I'm not seeing the problem.I disagree. It's critical for chaining one function to the next.I one can't return, one can't chain.
Dec 10 2014
On Thursday, 11 December 2014 at 07:30:03 UTC, Walter Bright wrote:On 12/10/2014 8:56 PM, deadalnix wrote:a.foo().bar() is the same as bar(foo(a)), that is, using the returned value as first parameter. If you can't return, you can't chain.On Thursday, 11 December 2014 at 03:27:08 UTC, Walter Bright wrote:I guess I'm not seeing the problem.I disagree. It's critical for chaining one function to the next.I one can't return, one can't chain.
Dec 10 2014
On 12/10/2014 11:55 PM, deadalnix wrote:On Thursday, 11 December 2014 at 07:30:03 UTC, Walter Bright wrote:Right, and you can chain when the declarations are: scope ref S foo(ref T a); U bar(ref S s); a.foo().bar(); // worksOn 12/10/2014 8:56 PM, deadalnix wrote:a.foo().bar() is the same as bar(foo(a)), that is, using the returned value as first parameter. If you can't return, you can't chain.On Thursday, 11 December 2014 at 03:27:08 UTC, Walter Bright wrote:I guess I'm not seeing the problem.I disagree. It's critical for chaining one function to the next.I one can't return, one can't chain.
Dec 11 2014
On 05/12/2014 23:58, Walter Bright wrote:From the DIP: "The lifetime of a scope return value is the lifetime of an rvalue. It may not be copied in a way that extends its life." With part of the example: scope int* foo(); ... int* p = foo(); // Error, lifetime(p) is ∞ Maybe the error should be 'scope return value cannot be stored', because otherwise p could be inferred as scope.2) `scope ref` return values cannot be stored. scope ref int foo(); void bar(scope ref int a); foo().bar(); // allowed scope tmp = foo(); // not allowed tmp.bar();Right
Dec 07 2014
On Friday, 5 December 2014 at 16:48:45 UTC, Marc Schütz wrote:There are limitations this proposal has in comparison to my original one. These limitations might of course be harmless and play no role in practice, but on the other hand, they may, so I think it's good to list them here.One concern I had with your proposal was that it refers to a symbol before it's available. scope!haystack(string) findSubstring(scope(string) haystack, scope(string) needle) C++11 had similar issues and they solved it by introducing trailing return types. ex: "template<class T> auto mul(T a, T b) -> decltype(a*b)" Personally I would prefer not to go down that lane and DIP69 avoids that problem.
Dec 06 2014
On Saturday, 6 December 2014 at 10:59:24 UTC, Daniel N wrote:On Friday, 5 December 2014 at 16:48:45 UTC, Marc Schütz wrote:For D, this wouldn't be necessary, because parsing and semantic analysis are strictly separated. The owners would only have to be evaluated very late during the semantic phase. But let's see how DIP69 works out...There are limitations this proposal has in comparison to my original one. These limitations might of course be harmless and play no role in practice, but on the other hand, they may, so I think it's good to list them here.One concern I had with your proposal was that it refers to a symbol before it's available. scope!haystack(string) findSubstring(scope(string) haystack, scope(string) needle) C++11 had similar issues and they solved it by introducing trailing return types. ex: "template<class T> auto mul(T a, T b) -> decltype(a*b)" Personally I would prefer not to go down that lane and DIP69 avoids that problem.
Dec 06 2014
On Thursday, 4 December 2014 at 09:25:11 UTC, Walter Bright wrote:http://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape. This, among other things, makes a ref counting type practical. It also makes it more practical to use other storage allocation schemes than garbage collection. It does not make scope into a type constructor, nor a general type-annotation system. It does not provide an ownership system, though it would complement one.Thanks a lot of trying to move forward with this. Also glad to see DIP36 didn't vanish completely useless :P It will take me some time to provide a detailed response so I'll post a summary of my impression first. I recognize and respect your attempt to go for most simple solution that is still useful in practice. Can't say I am happy about it but it is better to have something working than awesome plans that never get implemented. In this context trying to get most of scope as storage class seems right. But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting. There are also issues that pop up because of missing transitivity. Maybe this can be fixed within existing proposal, maybe not. Right now I don't have any strong opinion. I also don't consider `ref` design as a storage class any kind of success at all and generally agree with Manu on this topic. At the same time alternative proposals that make it a qualifier (like Marc did) do impact existing language much more and this no small concern. This won't be easy.
Dec 07 2014
On 12/7/2014 6:12 AM, Dicebot wrote:But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.I also don't consider `ref` design as a storage class any kind of success at all and generally agree with Manu on this topic. At the same time alternative proposals that make it a qualifier (like Marc did) do impact existing language much more and this no small concern.My experience with C++ ref as type qualifier is very, very bad. It's a special case EVERYWHERE. Doing type deduction with it is an exercise in a completely baffling set of rules and a different rule for every occasion - Scott Meyers has a great piece on this. There are probably only a handful of people on the planet who actually understand C++ ref. I wished very hard to avoid that with D ref.
Dec 07 2014
Walter Bright:There are probably only a handful of people on the planet who actually understand C++ ref. I wished very hard to avoid that with D ref.When C++ programmers say that D-style ranges can't do everything C++ iterators can do, they seem to miss that sometimes it's a good idea to adopt a simpler language feature, that doesn't cover 100% usages, if it covers 80-90% of the cases, and has a simpler syntax, and simpler semantics to understand for the programmer. (The comment above is not about DIP69). Bye, bearophile
Dec 07 2014
On 12/7/2014 2:58 PM, bearophile wrote:When C++ programmers say that D-style ranges can't do everything C++ iterators can do, they seem to miss that sometimes it's a good idea to adopt a simpler language feature, that doesn't cover 100% usages, if it covers 80-90% of the cases, and has a simpler syntax, and simpler semantics to understand for the programmer.I agree, but it's hard to find that sweet spot. I think Java definitely went too far, and Go went too far for my taste.(The comment above is not about DIP69).Yes, it is :-)
Dec 07 2014
On 12/7/14 4:29 PM, Walter Bright wrote:On 12/7/2014 6:12 AM, Dicebot wrote:So: int x; foo(x) += 1; will compile? I was under the impression this would be disallowed. If you do not connect the scope to the parameter, then the caller has to assume that variable can escape, and you can't use scoped variables as arguments at all.But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.My experience with C++ ref as type qualifier is very, very bad. It's a special case EVERYWHERE. Doing type deduction with it is an exercise in a completely baffling set of rules and a different rule for every occasion - Scott Meyers has a great piece on this. There are probably only a handful of people on the planet who actually understand C++ ref. I wished very hard to avoid that with D ref.D has so many features that did not exist when ref was created (as inout in D1), that you can ALMOST duplicate ref. The only thing we could not duplicate is the implicit address-taking on construction, which maybe is not such a bad thing. -Steve
Dec 08 2014
On 08/12/2014 15:53, Steven Schveighoffer wrote:Yes, because foo's argument is not scope, it can be returned.scope ref int foo(ref int x); will do it.So: int x; foo(x) += 1; will compile?
Dec 09 2014
On 12/9/14 9:23 AM, Nick Treleaven wrote:On 08/12/2014 15:53, Steven Schveighoffer wrote:But I thought if you take a reference from the stack, it's inferred as scope? I feel like there's a missing link somewhere if the scope is missing from the parameter. Will ref just automatically bind to any scoped reference? What about this: auto y = &x; foo(*y) += 1; -SteveYes, because foo's argument is not scope, it can be returned.scope ref int foo(ref int x); will do it.So: int x; foo(x) += 1; will compile?
Dec 09 2014
On 09/12/2014 16:25, Steven Schveighoffer wrote:But I thought if you take a reference from the stack, it's inferred as scope? I feel like there's a missing link somewhere if the scope is missing from the parameter.I think (with this DIP) values on the stack can safely be passed as a ref parameter, which is a bit more flexible than a scope parameter.Will ref just automatically bind to any scoped reference? What about this:(for reference:) int x; scope ref int foo(ref int x);auto y = &x; foo(*y) += 1;y gets inferred as scope, but *y is not scope, so it should work.
Dec 09 2014
On 09/12/2014 16:25, Steven Schveighoffer wrote:Will ref just automatically bind to any scoped reference?A ref parameter essentially is a scope ref parameter, but it can also be returned: http://forum.dlang.org/post/m64v3g$2bga$1 digitalmars.com
Dec 11 2014
On Thursday, 11 December 2014 at 14:12:16 UTC, Nick Treleaven wrote:On 09/12/2014 16:25, Steven Schveighoffer wrote:Introducing a keyword to be able to add arbitrary restriction that do not make the code safer, more optimization or anything is not paying for itself.Will ref just automatically bind to any scoped reference?A ref parameter essentially is a scope ref parameter, but it can also be returned: http://forum.dlang.org/post/m64v3g$2bga$1 digitalmars.com
Dec 11 2014
On Sunday, 7 December 2014 at 21:29:50 UTC, Walter Bright wrote:On 12/7/2014 6:12 AM, Dicebot wrote:This isn't the same as it does not propagate scope but just restricts return value. Difference is that it cannot be chained. Let's consider practical example based on Phobos: there was an issue with byLine range that it has reused same buffer internally which sometimes caught users off guard when trying to save slice. It is a natural fit for `scope` - make it return `scope string` instead to ensure that no slices get stored. Two issues immediately pop up: 1) scope is not transitive thus it doesn't work at all - you still can store slice of `scope string` as only actual ptr+length struct is protected. 2) even if it worked, existing definition of scope return value makes it impossible to use in typical idiomatic pipeline: `file.byLine.algo1.algo2`. Either algoX is defined to take `scope ref` and thus can't return it or it is defined to take `ref` and can't take another `scope ref` as an argument. At least this is what I get from reading existing examples in DIP69But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.While there is no argument that C++ ref is screwed, it is rather hard to say if this is inherent consequence of ref being a type qualifier or just C++ being C++. I mean how many C++ type system features in general are understood my more than a handful of people on the planet? For me `ref` is essentially just a different flavor of `*` - and if the latter can be part of type, I see no reasons why former can'tI also don't consider `ref` design as a storage class any kind of success at all and generally agree with Manu on this topic. At the same time alternative proposals that make it a qualifier (like Marc did) do impact existing language much more and this no small concern.My experience with C++ ref as type qualifier is very, very bad. It's a special case EVERYWHERE. Doing type deduction with it is an exercise in a completely baffling set of rules and a different rule for every occasion - Scott Meyers has a great piece on this. There are probably only a handful of people on the planet who actually understand C++ ref. I wished very hard to avoid that with D ref.
Dec 08 2014
On Monday, 8 December 2014 at 16:25:22 UTC, Dicebot wrote:On Sunday, 7 December 2014 at 21:29:50 UTC, Walter Bright wrote:That's why I asked the question in http://forum.dlang.org/post/xdjsmwocbtxovjnathek forum.dlang.org . It seems Walter wants to allow passing `scope ref` to `ref`, but then automatically treat a normal `ref` of the second function as if it were `scope ref`. But I can't quite see through it :-(On 12/7/2014 6:12 AM, Dicebot wrote:This isn't the same as it does not propagate scope but just restricts return value. Difference is that it cannot be chained. Let's consider practical example based on Phobos: there was an issue with byLine range that it has reused same buffer internally which sometimes caught users off guard when trying to save slice. It is a natural fit for `scope` - make it return `scope string` instead to ensure that no slices get stored. Two issues immediately pop up: 1) scope is not transitive thus it doesn't work at all - you still can store slice of `scope string` as only actual ptr+length struct is protected. 2) even if it worked, existing definition of scope return value makes it impossible to use in typical idiomatic pipeline: `file.byLine.algo1.algo2`. Either algoX is defined to take `scope ref` and thus can't return it or it is defined to take `ref` and can't take another `scope ref` as an argument.But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.At least this is what I get from reading existing examples in DIP69I think most problems would come from type deduction. All the other qualifiers and function attributes, like const and pure, just either work, or they don't, so they can be tested easily, and you want to apply these qualifiers wherever possible. This also applies to scope. But for `ref`, it's presence influences behaviour, therefore I think it would be best if it were never inferred implicitly. Actually that too is just what we do with pointers. We don't change non-pointer types to pointers automatically.While there is no argument that C++ ref is screwed, it is rather hard to say if this is inherent consequence of ref being a type qualifier or just C++ being C++. I mean how many C++ type system features in general are understood my more than a handful of people on the planet? For me `ref` is essentially just a different flavor of `*` - and if the latter can be part of type, I see no reasons why former can'tI also don't consider `ref` design as a storage class any kind of success at all and generally agree with Manu on this topic. At the same time alternative proposals that make it a qualifier (like Marc did) do impact existing language much more and this no small concern.My experience with C++ ref as type qualifier is very, very bad. It's a special case EVERYWHERE. Doing type deduction with it is an exercise in a completely baffling set of rules and a different rule for every occasion - Scott Meyers has a great piece on this. There are probably only a handful of people on the planet who actually understand C++ ref. I wished very hard to avoid that with D ref.
Dec 08 2014
On Monday, 8 December 2014 at 16:57:44 UTC, Marc Schütz wrote:Yes I have seen that thread and have no idea what Walter has meant either. His answer only makes sense if return value scope is supposed to be inferred from body - what when bodies are guaranteed to be there everything can be inferred anyway.Two issues immediately pop up: 1) scope is not transitive thus it doesn't work at all - you still can store slice of `scope string` as only actual ptr+length struct is protected. 2) even if it worked, existing definition of scope return value makes it impossible to use in typical idiomatic pipeline: `file.byLine.algo1.algo2`. Either algoX is defined to take `scope ref` and thus can't return it or it is defined to take `ref` and can't take another `scope ref` as an argument.That's why I asked the question in http://forum.dlang.org/post/xdjsmwocbtxovjnathek forum.dlang.org . It seems Walter wants to allow passing `scope ref` to `ref`, but then automatically treat a normal `ref` of the second function as if it were `scope ref`. But I can't quite see through it :-(
Dec 08 2014
On 12/8/2014 8:25 AM, Dicebot wrote:2) even if it worked, existing definition of scope return value makes it impossible to use in typical idiomatic pipeline: `file.byLine.algo1.algo2`. Either algoX is defined to take `scope ref` and thus can't return it or it is defined to take `ref` and can't take another `scope ref` as an argument. At least this is what I get from reading existing examples in DIP69The difference between 'scope ref' and 'ref' parameters is that the former cannot be returned by reference. The difference between 'scope ref' and 'ref' function returns is that the former cannot be saved by the caller. You can still safely pass the address of a stack variable by 'ref' - it will not escape. Under the current proposal, as now, you cannot store a ref by ref, and cannot take the address of a ref variable.It's not because C++ designers are idiots. It inevitably follows from what ref is. ref grew, brick by brick, into a monster, each brick being inevitable.There are probably only a handful of people on the planet who actually understand C++ ref. I wished very hard to avoid that with D ref.While there is no argument that C++ ref is screwed, it is rather hard to say if this is inherent consequence of ref being a type qualifier or just C++ being C++.I mean how many C++ type system features in general are understood my more than a handful of people on the planet? For me `ref` is essentially just a different flavor of `*` - and if the latter can be part of type, I see no reasons why former can'tI agree it's a seductively simple idea. The trouble starts happening when you start when making ref idempotent, when ref can only be at the 'head' of a data structure, when trying to do type deduction of a ref type (do you get the ref, or do you look 'through' the ref?), what happens with overloading, etc., and on and on.
Dec 08 2014
On Monday, 8 December 2014 at 19:44:48 UTC, Walter Bright wrote:The difference between 'scope ref' and 'ref' parameters is that the former cannot be returned by reference. The difference between 'scope ref' and 'ref' function returns is that the former cannot be saved by the caller. You can still safely pass the address of a stack variable by 'ref' - it will not escape. Under the current proposal, as now, you cannot store a ref by ref, and cannot take the address of a ref variable.Easier to go straight with pseudo-code: struct ByLine { scope string front(); // ... } auto byLine(File file) { return ByLine(file); } scope /* ref */ string foo(scope /* ref */ string input) { return input[1..$]; } void main() { auto r = file.byLine.map!foo; string s = r.front; // this should not compile string s = r.front.dup; // this should compile // how foo signature should look like for this to work? }But was there any reason why those traits (alien to type qualifiers) were pursued? What is the problem with `ref` simply meaning `non-null pointer` and allowing non-idempotent ref(ref(int))?I mean how many C++ type system features in general are understood my more than a handful of people on the planet? For me `ref` is essentially just a different flavor of `*` - and if the latter can be part of type, I see no reasons why former can'tI agree it's a seductively simple idea. The trouble starts happening when you start when making ref idempotent, when ref can only be at the 'head' of a data structure, when trying to do type deduction of a ref type (do you get the ref, or do you look 'through' the ref?), what happens with overloading, etc., and on and on.
Dec 08 2014
On 12/8/2014 12:54 PM, Dicebot wrote:On Monday, 8 December 2014 at 19:44:48 UTC, Walter Bright wrote:Because it isn't just a non-null pointer (and ref's can still be null in C++!), it's an auto-dereferencing pointer.I agree it's a seductively simple idea. The trouble starts happening when you start when making ref idempotent, when ref can only be at the 'head' of a data structure, when trying to do type deduction of a ref type (do you get the ref, or do you look 'through' the ref?), what happens with overloading, etc., and on and on.But was there any reason why those traits (alien to type qualifiers) were pursued? What is the problem with `ref` simply meaning `non-null pointer` and allowing non-idempotent ref(ref(int))?
Dec 08 2014
Walter Bright <newshound2 digitalmars.com> wrote: [...]and ref's can still be null in C++!AFAIK only if you dereference a NULL pointer, which is UB. So not really. [...] Tobi
Dec 08 2014
On 12/8/2014 1:59 PM, Tobias Müller wrote:Walter Bright <newshound2 digitalmars.com> wrote: [...]Saying it's UB doesn't help in the slightest. You can still have null ref's, and no compiler will prevent it.and ref's can still be null in C++!AFAIK only if you dereference a NULL pointer, which is UB. So not really.
Dec 08 2014
On Monday, 8 December 2014 at 21:07:39 UTC, Walter Bright wrote:Don't want to distract from topic that actually matters but would be nice to ask you few more question on topic at some point.But was there any reason why those traits (alien to type qualifiers) were pursued? What is the problem with `ref` simply meaning `non-null pointer` and allowing non-idempotent ref(ref(int))?Because it isn't just a non-null pointer (and ref's can still be null in C++!), it's an auto-dereferencing pointer.
Dec 08 2014
On 12/8/2014 12:54 PM, Dicebot wrote:struct ByLine { scope string front(); // ... } auto byLine(File file) { return ByLine(file); } scope /* ref */ string foo(scope /* ref */ string input) { return input[1..$]; } void main() { auto r = file.byLine.map!foo; string s = r.front; // this should not compile string s = r.front.dup; // this should compile // how foo signature should look like for this to work? }front() should return a 'scope ref string'.
Dec 08 2014
On Monday, 8 December 2014 at 21:12:47 UTC, Walter Bright wrote:On 12/8/2014 12:54 PM, Dicebot wrote:That seems to contradict your other statement:struct ByLine { scope string front(); // ... } auto byLine(File file) { return ByLine(file); } scope /* ref */ string foo(scope /* ref */ string input) { return input[1..$]; } void main() { auto r = file.byLine.map!foo; string s = r.front; // this should not compile string s = r.front.dup; // this should compile // how foo signature should look like for this to work? }front() should return a 'scope ref string'.A 'scope ref' parameter may not be returned as a 'ref' or a 'scope ref'.Please check `foo()` once more - it needs to accept scope (ref) to be able to accept ByLine.front as an argument. And it also needs to pass it down the call chain - but returning `input` by reference is illegal according to abovementioned rule.
Dec 08 2014
On 12/8/2014 3:14 PM, Dicebot wrote:On Monday, 8 December 2014 at 21:12:47 UTC, Walter Bright wrote:Just make it a 'ref' parameter.On 12/8/2014 12:54 PM, Dicebot wrote:That seems to contradict your other statement:struct ByLine { scope string front(); // ... } auto byLine(File file) { return ByLine(file); } scope /* ref */ string foo(scope /* ref */ string input) { return input[1..$]; } void main() { auto r = file.byLine.map!foo; string s = r.front; // this should not compile string s = r.front.dup; // this should compile // how foo signature should look like for this to work? }front() should return a 'scope ref string'.A 'scope ref' parameter may not be returned as a 'ref' or a 'scope ref'.Please check `foo()` once more - it needs to accept scope (ref) to be able to accept ByLine.front as an argument. And it also needs to pass it down the call chain - but returning `input` by reference is illegal according to abovementioned rule.It can still be passed down as a 'ref' parameter.
Dec 09 2014
On Tuesday, 9 December 2014 at 22:24:51 UTC, Walter Bright wrote:But as far as I understand the spec it will result it this code failing too: auto r = ["aaa", "bbb", "ccc"].map!foo; // should compile but will fail because foo returns scope ref: string s = r.front; What I mean is that in current proposal it is impossible to transfer scope information down the call chain - either function always returns scope ref or never. Which implies the necessity of something like `auto scope`...Just make it a 'ref' parameter.front() should return a 'scope ref string'.That seems to contradict your other statement:A 'scope ref' parameter may not be returned as a 'ref' or a 'scope ref'.Please check `foo()` once more - it needs to accept scope (ref) to be able to accept ByLine.front as an argument. And it also needs to pass it down the call chain - but returning `input` by reference is illegal according to abovementioned rule.It can still be passed down as a 'ref' parameter.
Dec 09 2014
On 12/9/2014 8:34 PM, Dicebot wrote:But as far as I understand the spec it will result it this code failing too: auto r = ["aaa", "bbb", "ccc"].map!foo; // should compile but will fail because foo returns scope ref: string s = r.front; What I mean is that in current proposal it is impossible to transfer scope information down the call chain - either function always returns scope ref or never. Which implies the necessity of something like `auto scope`...If you want data to 'escape' from r.front, then it shouldn't be marked as scope. Definitely, using scope successfully will require some rethinking of how code is written.
Dec 10 2014
On Thursday, 11 December 2014 at 03:30:07 UTC, Walter Bright wrote:If you want data to 'escape' from r.front, then it shouldn't be marked as scope. Definitely, using scope successfully will require some rethinking of how code is written.Allowing or prohibiting escaping of r.front in that example depends on definition of mapped range. There is nothing to "rethink" about it - case is almost identical to const+inout.
Dec 10 2014
On 12/10/2014 10:43 PM, Dicebot wrote:On Thursday, 11 December 2014 at 03:30:07 UTC, Walter Bright wrote:One reason why with templates, they'll do scope inference.If you want data to 'escape' from r.front, then it shouldn't be marked as scope. Definitely, using scope successfully will require some rethinking of how code is written.Allowing or prohibiting escaping of r.front in that example depends on definition of mapped range. There is nothing to "rethink" about it - case is almost identical to const+inout.
Dec 11 2014
On Thursday, 11 December 2014 at 08:30:55 UTC, Walter Bright wrote:On 12/10/2014 10:43 PM, Dicebot wrote:So the answer really is "current proposal does not support it"? Well this alone seems limiting enough for me to kills the whole DIP. "Use templates everywhere" is not really an answer, we could as well just prohibit separate compilation without .di files and enjoy full source availability as inherent requirement.On Thursday, 11 December 2014 at 03:30:07 UTC, Walter Bright wrote:One reason why with templates, they'll do scope inference.If you want data to 'escape' from r.front, then it shouldn't be marked as scope. Definitely, using scope successfully will require some rethinking of how code is written.Allowing or prohibiting escaping of r.front in that example depends on definition of mapped range. There is nothing to "rethink" about it - case is almost identical to const+inout.
Dec 11 2014
On 12/11/2014 10:04 PM, Dicebot wrote:On Thursday, 11 December 2014 at 08:30:55 UTC, Walter Bright wrote:When you're relying on the definition of an argument, it is inherently a template.On 12/10/2014 10:43 PM, Dicebot wrote:So the answer really is "current proposal does not support it"? Well this alone seems limiting enough for me to kills the whole DIP. "Use templates everywhere" is not really an answer, we could as well just prohibit separate compilation without .di files and enjoy full source availability as inherent requirement.On Thursday, 11 December 2014 at 03:30:07 UTC, Walter Bright wrote:One reason why with templates, they'll do scope inference.If you want data to 'escape' from r.front, then it shouldn't be marked as scope. Definitely, using scope successfully will require some rethinking of how code is written.Allowing or prohibiting escaping of r.front in that example depends on definition of mapped range. There is nothing to "rethink" about it - case is almost identical to const+inout.
Dec 11 2014
On Monday, 8 December 2014 at 20:54:54 UTC, Dicebot wrote:But was there any reason why those traits (alien to type qualifiers) were pursued? What is the problem with `ref` simply meaning `non-null pointer` and allowing non-idempotent ref(ref(int))?Please no. when you do int a; and then use a, you always either refer to a, to memory storage (lvalue) or a, the value stored in that memory (the rvalue). When doing ref int a = xxx; You specify that you don't create a new storage for a, but that you must consider xxx as an lvalue, and bind the name a to that same lvalue. Once you get that, you get why ref(ref(int)) do not make any sense, and is generally undesirable.
Dec 08 2014
On Monday, 8 December 2014 at 23:19:26 UTC, deadalnix wrote:On Monday, 8 December 2014 at 20:54:54 UTC, Dicebot wrote:My thoughts have went totally different direction: ref(int) a = &xxx; // ok ref(int) a = xxx; // bad, can't initialize ref from value ref(int) a = intptr; // ok, implicitly inject assert(intptr) ref(int) a = null; // bad, can't initialize ref from literal Now I see how issue may arise because of implicit conversion to matching value type (when passing to as function argument for example) but we do already have structs with `alias this` which may need similar semantics - it needs to be defined anyway. This is still offtopic. I support attempt to make scope a storage class in general because this is an approach of minimal change and still can be useful. Problem with defining it so that it actually is useful.But was there any reason why those traits (alien to type qualifiers) were pursued? What is the problem with `ref` simply meaning `non-null pointer` and allowing non-idempotent ref(ref(int))?Please no. when you do int a; and then use a, you always either refer to a, to memory storage (lvalue) or a, the value stored in that memory (the rvalue). When doing ref int a = xxx; You specify that you don't create a new storage for a, but that you must consider xxx as an lvalue, and bind the name a to that same lvalue.
Dec 08 2014
On Monday, 8 December 2014 at 16:25:22 UTC, Dicebot wrote:This isn't the same as it does not propagate scope but just restricts return value. Difference is that it cannot be chained. Let's consider practical example based on Phobos: there was an issue with byLine range that it has reused same buffer internally which sometimes caught users off guard when trying to save slice. It is a natural fit for `scope` - make it return `scope string` instead to ensure that no slices get stored.That would only ensure that slice do not outline the range.Two issues immediately pop up: 1) scope is not transitive thus it doesn't work at all - you still can store slice of `scope string` as only actual ptr+length struct is protected.Yes, that is my whole point whith the scope flag in expression. Without it, the proposal need hacks to work (&(*e)) and is too restrictive to be useful.While there is no argument that C++ ref is screwed, it is rather hard to say if this is inherent consequence of ref being a type qualifier or just C++ being C++. I mean how many C++ type system features in general are understood my more than a handful of people on the planet? For me `ref` is essentially just a different flavor of `*` - and if the latter can be part of type, I see no reasons why former can'tNo, a type constructor is not the right tool. This about it. scope(int)[] do not make any sense as the slice cannot outlive its content safely. You need to have a flag on expression and declarations to make this work.
Dec 08 2014
On Sunday, 7 December 2014 at 21:29:50 UTC, Walter Bright wrote:My experience with C++ ref as type qualifier is very, very bad. It's a special case EVERYWHERE. Doing type deduction with it is an exercise in a completely baffling set of rules and a different rule for every occasion - Scott Meyers has a great piece on this. There are probably only a handful of people on the planet who actually understand C++ ref. I wished very hard to avoid that with D ref.Type qualifier for scope is the wrong tool. scope(int)[] do not make any sense. The slice cannot outlive its content. scope as a flag on expressions/symbols is very useful.
Dec 08 2014
On Monday, 8 December 2014 at 23:05:49 UTC, deadalnix wrote:scope(int)[] do not make any sense. The slice cannot outlive its content. scope as a flag on expressions/symbols is very useful.I'd say it strange for directly the opposite reason - pure value type is inherently scope so adding such annotation shouldn't matter in general.
Dec 08 2014
On 8 December 2014 at 07:29, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 12/7/2014 6:12 AM, Dicebot wrote:Will it? It looks like foo can't be called with scope data?But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.I do completely sympathise with you on this point. I understand where you were coming from on 'ref', and I do applaud the effort. Nobody said that ref should be just the same as C++, but I am saying that the D solution is not a success. I'm all about a ref that improves on C++, but I don't think that's what we have. We have something that's just complex and unwieldy in a different (and for my money, even more frustrating) way. I'm just saying, don't repeat that mistake with scope! There must be another way... I'm also quite uneasy with the fact that scope would not be transitive as a storage class. What happens when it's applied to a value type, like a struct, that contains some pointers? An adaptation wrapper for a single pointer is super common; ie, a tiny struct passed by value with scope needs to have it's contained pointer receive the scope-ness of the argument. I don't have the perfect proposal, but I feel very strongly about 2 things: 1. It must not be a storage class; the concept was a disaster with ref, and I struggle with this more frequently than any other 'feature' in D. 2. I feel it's a big mistake to separate it from the type system, which I think most agree, is D's greatest asset by far. Manipulating types is simple and convenient using the type system, and I think manipulating scope will be just as important as any other attribute as soon as even slightly complex use cases begin to arise.I also don't consider `ref` design as a storage class any kind of success at all and generally agree with Manu on this topic. At the same time alternative proposals that make it a qualifier (like Marc did) do impact existing language much more and this no small concern.My experience with C++ ref as type qualifier is very, very bad. It's a special case EVERYWHERE. Doing type deduction with it is an exercise in a completely baffling set of rules and a different rule for every occasion - Scott Meyers has a great piece on this. There are probably only a handful of people on the planet who actually understand C++ ref. I wished very hard to avoid that with D ref.
Dec 11 2014
Manu have you shown Walter some of the code where ref is problematic? He seems to have asked to see examples repeatedly as he's not convinced there is a problem.
Dec 11 2014
On 11 December 2014 at 23:22, ixid via Digitalmars-d <digitalmars-d puremagic.com> wrote:Manu have you shown Walter some of the code where ref is problematic? He seems to have asked to see examples repeatedly as he's not convinced there is a problem.I have discussed issues here in the past, but possibly not as concretely as would be useful to this discussion. I want to take some time to contribute value to this issue, but I have virtually no time at all lately, and I can't even keep up with this posts thread, let alone sit and consider+craft a bunch of demonstration cases. This thread is moving faster than I am able to, so I'm not sure what I can do. There's no TL;DR conversation, and almost all discussion happens outside of my timezone, which wasn't such a problem when I was nocturnal, but I have had to change lifestyle recently :/ I have about 30 posts to try and catch up on and understand right now in 5 minutes before I have to leave >_<
Dec 12 2014
On Thursday, 11 December 2014 at 12:48:05 UTC, Manu via Digitalmars-d wrote:On 8 December 2014 at 07:29, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:This is a point that most people don't seem to understand yet, and which wasn't obvious for me either, at the beginning: * A `ref` parameter means that it cannot escape the function, _except_ by return. * A `scope ref` parameter means that it cannot escape the function _ever_, not even by return. * A `scope ref` return means that it cannot leave the current statement. Therefore, a `scope ref` return value can be passed on to the next function as a `ref` argument. If that function again returns a reference (even if not explicitly designated as `scope`), the compiler will treat it as if it were `scope ref`.On 12/7/2014 6:12 AM, Dicebot wrote:Will it? It looks like foo can't be called with scope data?But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.I'm also quite uneasy with the fact that scope would not be transitive as a storage class. What happens when it's applied to a value type, like a struct, that contains some pointers? An adaptation wrapper for a single pointer is super common; ie, a tiny struct passed by value with scope needs to have it's contained pointer receive the scope-ness of the argument.I agree, this is important. In my proposal, this works without transitivity. The wrapper stores the pointer as a `scope` member, then by copying the wrapper, the pointer gets copied implicitly, to which the normal scope restrictions apply (`scope` on members means "will not outlive the aggregate"). If it stored it as normal non-scope pointer, it couldn't get assigned in the first place. (Additionally, some higher level tricks are possible if we allow scope for value types and overloading on scope.)
Dec 11 2014
On Thursday, 11 December 2014 at 13:55:55 UTC, Marc Schütz wrote:On Thursday, 11 December 2014 at 12:48:05 UTC, Manu via Digitalmars-d wrote:OH! I had totally misunderstood that. Cheers for the explanation.On 8 December 2014 at 07:29, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:This is a point that most people don't seem to understand yet, and which wasn't obvious for me either, at the beginning: * A `ref` parameter means that it cannot escape the function, _except_ by return. * A `scope ref` parameter means that it cannot escape the function _ever_, not even by return. * A `scope ref` return means that it cannot leave the current statement. Therefore, a `scope ref` return value can be passed on to the next function as a `ref` argument. If that function again returns a reference (even if not explicitly designated as `scope`), the compiler will treat it as if it were `scope ref`.On 12/7/2014 6:12 AM, Dicebot wrote:Will it? It looks like foo can't be called with scope data?But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.
Dec 11 2014
On 12/11/14 8:55 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>" wrote:On Thursday, 11 December 2014 at 12:48:05 UTC, Manu via Digitalmars-d wrote:Please, put this in the DIP! This is exactly the thing I had been asking for here: http://forum.dlang.org/post/m5sitd$1ki0$1 digitalmars.com Thanks, I'll re-read the proposal with this in mind. -SteveOn 8 December 2014 at 07:29, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:This is a point that most people don't seem to understand yet, and which wasn't obvious for me either, at the beginning: * A `ref` parameter means that it cannot escape the function, _except_ by return. * A `scope ref` parameter means that it cannot escape the function _ever_, not even by return. * A `scope ref` return means that it cannot leave the current statement. Therefore, a `scope ref` return value can be passed on to the next function as a `ref` argument. If that function again returns a reference (even if not explicitly designated as `scope`), the compiler will treat it as if it were `scope ref`.On 12/7/2014 6:12 AM, Dicebot wrote:Will it? It looks like foo can't be called with scope data?But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.
Dec 11 2014
On 12/11/2014 8:26 AM, Steven Schveighoffer wrote:Please, put this in the DIP! This is exactly the thing I had been asking for here: http://forum.dlang.org/post/m5sitd$1ki0$1 digitalmars.com Thanks, I'll re-read the proposal with this in mind.Marc is clearly better at explaining things than I am, as I've explained that point in this thread about 5 times now.
Dec 11 2014
On Thursday, 11 December 2014 at 13:55:55 UTC, Marc Schütz wrote:On Thursday, 11 December 2014 at 12:48:05 UTC, Manu via Digitalmars-d wrote:No, it understood. It is simply not useful.On 8 December 2014 at 07:29, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:This is a point that most people don't seem to understand yet, and which wasn't obvious for me either, at the beginning: * A `ref` parameter means that it cannot escape the function, _except_ by return. * A `scope ref` parameter means that it cannot escape the function _ever_, not even by return. * A `scope ref` return means that it cannot leave the current statement. Therefore, a `scope ref` return value can be passed on to the next function as a `ref` argument. If that function again returns a reference (even if not explicitly designated as `scope`), the compiler will treat it as if it were `scope ref`.On 12/7/2014 6:12 AM, Dicebot wrote:Will it? It looks like foo can't be called with scope data?But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.I agree, this is important. In my proposal, this works without transitivity. The wrapper stores the pointer as a `scope` member, then by copying the wrapper, the pointer gets copied implicitly, to which the normal scope restrictions apply (`scope` on members means "will not outlive the aggregate"). If it stored it as normal non-scope pointer, it couldn't get assigned in the first place.Wut ? You cante store anything with grear lifetime, including non scope things (as they'll have infinite lifetime). Meaning the only thing you know, is that thing are possibly scoped. Meaning you have to assume infinite lifetime with every indirection, which make this proposal useless.
Dec 11 2014
On Friday, 12 December 2014 at 00:13:10 UTC, deadalnix wrote:So far I tend to agree, unfortunately. Considering all provided answers and explanations suggested semantics are simply not powerful enough to be useful even for relatively simple idiomatic D code and with no clear way of backward compatible improvements this DIP does not pull own weight. "Those are not scopes you are looking for"Therefore, a `scope ref` return value can be passed on to the next function as a `ref` argument. If that function again returns a reference (even if not explicitly designated as `scope`), the compiler will treat it as if it were `scope ref`.No, it understood. It is simply not useful.
Dec 11 2014
On 12/11/2014 10:10 PM, Dicebot wrote:So far I tend to agree, unfortunately. Considering all provided answers and explanations suggested semantics are simply not powerful enough to be useful even for relatively simple idiomatic D code and with no clear way of backward compatible improvements this DIP does not pull own weight. "Those are not scopes you are looking for"The proposal provides escape proof passing of arguments. This is necessary in order to make rc safe, for example. What are you looking for?
Dec 12 2014
On 2014-12-13 01:09, Walter Bright wrote:The proposal provides escape proof passing of arguments. This is necessary in order to make rc safe, for example. What are you looking for?Passing references to stack allocated data to functions safely is one idea that comes to mind. -- /Jacob Carlborg
Dec 13 2014
On 12/13/2014 8:01 AM, Jacob Carlborg wrote:On 2014-12-13 01:09, Walter Bright wrote:'scope ref' does that.The proposal provides escape proof passing of arguments. This is necessary in order to make rc safe, for example. What are you looking for?Passing references to stack allocated data to functions safely is one idea that comes to mind.
Dec 13 2014
On Saturday, 13 December 2014 at 00:10:07 UTC, Walter Bright wrote:The proposal provides escape proof passing of arguments. This is necessary in order to make rc safe, for example. What are you looking for?I am looking for a tool to prevent escaping references to user-defined entities / resources from specific scope. RC case is not interesting at all to me though I recognize the potential benefits of tweaking the scope system to help with it. But there is a big difference between tweak for one specific use case or designing feature around it (so it becomes almost useless in other cases). I have already provided you an example of code I want to be enforced with scope and two major issues with existing proposal that make it lacking. This example is akin to litmus test for any scope proposal - if it doesn't allow to express such design, that proposal is simply not good enough. In case you have forgotten, I am reminding about two critical points that are necessary to make it fly: 1) inout analog for scope to be able to deduce borrowship of return values based on borrowship of input arguments. Current system is conservative and results in template bloat (complicated by the fact that it is storage class thus not actually part of a type) 2) at least optional transitivity to be able to express to protect with scope data referenced by slice or owned linked list referenced from root node. ------ In your tree example I would have never wanted scope protection of one specific node of such tree - but a transitive scope protection of whole tree view available through on of node pointers/references. It doesn't matter who and how owns the data for borrowship implementation - only thing that matters that _it is not me_.
Dec 14 2014
On 12/14/2014 5:44 PM, Dicebot wrote:I am looking for a tool to prevent escaping references to user-defined entities / resources from specific scope. RC case is not interesting at all to me though I recognize the potential benefits of tweaking the scope system to help with it. But there is a big difference between tweak for one specific use case or designing feature around it (so it becomes almost useless in other cases). I have already provided you an example of code I want to be enforced with scope and two major issues with existing proposal that make it lacking. This example is akin to litmus test for any scope proposal - if it doesn't allow to express such design, that proposal is simply not good enough. In case you have forgotten, I am reminding about two critical points that are necessary to make it fly: 1) inout analog for scope to be able to deduce borrowship of return values based on borrowship of input arguments. Current system is conservative and results in template bloat (complicated by the fact that it is storage class thus not actually part of a type)1. All inout actually does is reduce code copy/pasta, it is not critical. 2. This is what scope inference is all about.2) at least optional transitivity to be able to express to protect with scope data referenced by slice or owned linked list referenced from root node.1. that won't work unless scope is a type constructor 2. it can be achieved by using wrappers that only allow by-scope references to their data (RC is an example of such a wrapper)In your tree example I would have never wanted scope protection of one specific node of such tree - but a transitive scope protection of whole tree view available through on of node pointers/references. It doesn't matter who and how owns the data for borrowship implementation - only thing that matters that _it is not me_.As I explained to Manu, transitive scope makes things like doing a symbol table lookup, then putting a pointer to the found symbol in an AST, not workable.
Dec 14 2014
On Monday, 15 December 2014 at 02:45:04 UTC, Walter Bright wrote:1. All inout actually does is reduce code copy/pasta, it is not critical.Being forced to duplicate every single function in two flavors to actually make scope system usable? This is as much critical as it can be.2. This is what scope inference is all about.Which only works with templates and lack of scope on arguments does not affect function body -> templates are not necessary, same as inout.I know and this is why I am leaning toward it being qualifier despite all related issues.2) at least optional transitivity to be able to express to protect with scope data referenced by slice or owned linked list referenced from root node.1. that won't work unless scope is a type constructor2. it can be achieved by using wrappers that only allow by-scope references to their data (RC is an example of such a wrapper)It is viral approach and backwards incompatible one - we can't change signatures of Phobos functions to return Wrapped!(char[]) instead of char[] for example. While theoretically possible I can't call this approach practical - not until see some convincing application example.This sounds totally against my understanding of scope. I want scope exactly to prohibit such actions. However it is possible in slightly modified way - where you don't directly insert pointer to AST but use public method of AST control structure that checks if supplied scope pointer belongs to list of nodes it owns and casts away scope after that.In your tree example I would have never wanted scope protection of one specific node of such tree - but a transitive scope protection of whole tree view available through on of node pointers/references. It doesn't matter who and how owns the data for borrowship implementation - only thing that matters that _it is not me_.As I explained to Manu, transitive scope makes things like doing a symbol table lookup, then putting a pointer to the found symbol in an AST, not workable.
Dec 14 2014
On 12/14/2014 7:41 PM, Dicebot wrote:On Monday, 15 December 2014 at 02:45:04 UTC, Walter Bright wrote:C++ seems to do fine without it for const. It's a convenience feature.1. All inout actually does is reduce code copy/pasta, it is not critical.Being forced to duplicate every single function in two flavors to actually make scope system usable? This is as much critical as it can be.It also works with all the lambdas, since source for them is always available. I also wanted to make it (i.e. inference) work with auto functions, but Don Clugston was the primary objector :-) I do view inference as something we need to extend to more functions.2. This is what scope inference is all about.Which only works with templates and lack of scope on arguments does not affect function body -> templates are not necessary, same as inout.That would be a truly massive change to D, and I'm not at all sure it would be worth it. We've (i.e. Kenji) have been fixing bugs with inout for years, and idea that had seemed straightforward.I know and this is why I am leaning toward it being qualifier despite all related issues.2) at least optional transitivity to be able to express to protect with scope data referenced by slice or owned linked list referenced from root node.1. that won't work unless scope is a type constructorBut you can change signatures to add annotations? You're going to get one or the other.2. it can be achieved by using wrappers that only allow by-scope references to their data (RC is an example of such a wrapper)It is viral approach and backwards incompatible one - we can't change signatures of Phobos functions to return Wrapped!(char[]) instead of char[] for example. While theoretically possible I can't call this approach practical - not until see some convincing application example.This sounds totally against my understanding of scope. I want scope exactly to prohibit such actions. However it is possible in slightly modified way - where you don't directly insert pointer to AST but use public method of AST control structure that checks if supplied scope pointer belongs to list of nodes it owns and casts away scope after that.Wrappers do that, too. I know that 'wrapper' sounds bad to you, but one of the goals of D's UDT's is to make it easy to adapt the behavior of a type by wrapping it in a struct, rather than build new behavior into the language.
Dec 14 2014
On Monday, 15 December 2014 at 06:12:05 UTC, Walter Bright wrote:C++ seems to do fine without it for const. It's a convenience feature.C++ const does not really restrict or affect anything "for real", it is non-existent feature. `scope` as proposed would result in inability to store result of predicates if those are ever to accept scope data - unless you defined two versions for each function that may possibly accept scope data with absolutely identical body.This is not really answering my objections but side-stepping. I do support attribute inference. I would actually support full attribute inference in the language (and help convincing Don about it ;)) if it was introduced as foundation of the language and not arbitrary hack (== rethink the way .di interfaces and static libraries are defined)It also works with all the lambdas, since source for them is always available. I also wanted to make it (i.e. inference) work with auto functions, but Don Clugston was the primary objector :-)2. This is what scope inference is all about.Which only works with templates and lack of scope on arguments does not affect function body -> templates are not necessary, same as inout.I do view inference as something we need to extend to more functions.That would help with many issues but it need careful design to be well-accepted. How about teaming up to do a DIP about it at some point where this discussion is over? :PYes, I know and this why I am leaning towards that path but willing to accept any storage-class based solution - as long as it fits my basic criteria. I wonder if some hybrid approach is possible - for example, keep it storage class but pretend anything transitively accessible though it as if it was of scope storage class on its own. But that quickly gets into "exceptions for special cases" land :(That would be a truly massive change to D, and I'm not at all sure it would be worth it. We've (i.e. Kenji) have been fixing bugs with inout for years, and idea that had seemed straightforward.I know and this is why I am leaning toward it being qualifier despite all related issues.2) at least optional transitivity to be able to express to protect with scope data referenced by slice or owned linked list referenced from root node.1. that won't work unless scope is a type constructorSuch annotation would only prevent from compiling code which was already undefined by documentation. Changing return type (which wasn't Voldemort type before) may break any program that has lines akin to `char[] data = foo()` - perfectly legal working code.But you can change signatures to add annotations?2. it can be achieved by using wrappers that only allow by-scope references to their data (RC is an example of such a wrapper)It is viral approach and backwards incompatible one - we can't change signatures of Phobos functions to return Wrapped!(char[]) instead of char[] for example. While theoretically possible I can't call this approach practical - not until see some convincing application example.Problem is that our UDT are simply not that good enough yet. Wrappers are fine in general but I want to wrap for less common cases and support more common ones natively. I suppose this is your intention too but we disagree on what is actually the common case and what is the special case.This sounds totally against my understanding of scope. I want scope exactly to prohibit such actions. However it is possible in slightly modified way - where you don't directly insert pointer to AST but use public method of AST control structure that checks if supplied scope pointer belongs to list of nodes it owns and casts away scope after that.Wrappers do that, too. I know that 'wrapper' sounds bad to you, but one of the goals of D's UDT's is to make it easy to adapt the behavior of a type by wrapping it in a struct, rather than build new behavior into the language.
Dec 15 2014
On 12/15/2014 1:28 AM, Dicebot wrote:On Monday, 15 December 2014 at 06:12:05 UTC, Walter Bright wrote:C++ programmers constantly copy/pasta functions for the sole purpose of having one with const and one without.C++ seems to do fine without it for const. It's a convenience feature.C++ const does not really restrict or affect anything "for real", it is non-existent feature.`scope` as proposed would result in inability to store result of predicates if those are ever to accept scope data - unless you defined two versions for each function that may possibly accept scope data with absolutely identical body.If that is necessary, it is no worse than C++ is with const. It isn't a critical issue, or C++ would not be usable. I'd like to wait and see how this plays out.Inference addresses the issue.This is not really answering my objections but side-stepping.It also works with all the lambdas, since source for them is always available. I also wanted to make it (i.e. inference) work with auto functions, but Don Clugston was the primary objector :-)2. This is what scope inference is all about.Which only works with templates and lack of scope on arguments does not affect function body -> templates are not necessary, same as inout.That would help with many issues but it need careful design to be well-accepted. How about teaming up to do a DIP about it at some point where this discussion is over? :POk, sure.
Dec 15 2014
On Monday, 15 December 2014 at 06:12:05 UTC, Walter Bright wrote:On 12/14/2014 7:41 PM, Dicebot wrote:It's more than that. It has the potential (well, could have) to be used for type erasure, which is a useful and worthwhile concept by itself.Being forced to duplicate every single function in two flavors to actually make scope system usable? This is as much critical as it can be.C++ seems to do fine without it for const. It's a convenience feature.To be honest, I think one reason for those bugs is the awkward implementation. I had a look at mtype.c to see how difficult it would be to add another type modifier. It's no fun - the current implementation requires a dozen groups of helper functions to be written for every possible combination of type modifiers. Therefore there are helpers like `makeConst`, `makeImmutable`, `makeShared`, `makeSharedConst`, `makeWild` (this is `inout`), `makeWildConst`, `makeSharedWild`, `makeSharedWildConst`, and `makeMutable`. There are also some monster switch statements in the same fashion. No wonder this is error prone. I suspect that with a cleaner implementation most of these problems will go away.I know and this is why I am leaning toward it being qualifier despite all related issues.That would be a truly massive change to D, and I'm not at all sure it would be worth it. We've (i.e. Kenji) have been fixing bugs with inout for years, and idea that had seemed straightforward.
Dec 15 2014
On 12/15/2014 3:34 AM, "Marc Schütz" <schuetzm gmx.net>" wrote:No wonder this is error prone. I suspect that with a cleaner implementation most of these problems will go away.Sometimes, it isn't possible to see a cleaner way unless the dirty version is written first. Sometimes, there isn't a cleaner way :-( But there are more issues with inout than are in mtype.c. The template implementation is full of it.
Dec 15 2014
On Monday, 15 December 2014 at 01:44:27 UTC, Dicebot wrote:On Saturday, 13 December 2014 at 00:10:07 UTC, Walter Bright wrote:Is it be possible to make `inout` a more general tool for type erasure? I don't think yet another ad-hoc mechanism should be introduced.The proposal provides escape proof passing of arguments. This is necessary in order to make rc safe, for example. What are you looking for?I am looking for a tool to prevent escaping references to user-defined entities / resources from specific scope. RC case is not interesting at all to me though I recognize the potential benefits of tweaking the scope system to help with it. But there is a big difference between tweak for one specific use case or designing feature around it (so it becomes almost useless in other cases). I have already provided you an example of code I want to be enforced with scope and two major issues with existing proposal that make it lacking. This example is akin to litmus test for any scope proposal - if it doesn't allow to express such design, that proposal is simply not good enough. In case you have forgotten, I am reminding about two critical points that are necessary to make it fly: 1) inout analog for scope to be able to deduce borrowship of return values based on borrowship of input arguments. Current system is conservative and results in template bloat (complicated by the fact that it is storage class thus not actually part of a type)2) at least optional transitivity to be able to express to protect with scope data referenced by slice or owned linked list referenced from root node. ------ In your tree example I would have never wanted scope protection of one specific node of such tree - but a transitive scope protection of whole tree view available through on of node pointers/references. It doesn't matter who and how owns the data for borrowship implementation - only thing that matters that _it is not me_.I think there are two cases that are relevant. 1) the tree nodes don't own their children, instead they are managed by - let's say - a region allocator, and 2) the tree nodes do own their children. In both cases, they can declare the children as scope members (assuming a proposal that allows that). The only case I can think of where the children should not be scope members is when they are GC managed. But in this case, you don't need to worry about references to them escaping. Therefore, I don't see a use case for transitivity of scope, from a conceptional point of view. If a particular design makes transitivity necessary, this points to a flaw in the design, IMO.
Dec 15 2014
On Monday, 15 December 2014 at 11:23:28 UTC, Marc Schütz wrote:I think there are two cases that are relevant. 1) the tree nodes don't own their children, instead they are managed by - let's say - a region allocator, and 2) the tree nodes do own their children. In both cases, they can declare the children as scope members (assuming a proposal that allows that). The only case I can think of where the children should not be scope members is when they are GC managed. But in this case, you don't need to worry about references to them escaping. Therefore, I don't see a use case for transitivity of scope, from a conceptional point of view. If a particular design makes transitivity necessary, this points to a flaw in the design, IMO.Actually I think in neither case children should be declared as scope members. Instead those should be declared as private members but methods that return pointers/references to children would be declared to return (transitive) scope pointers/references. For me "scopeness" is a property of "view", not object itself - this also makes ownership method of actual data irrelevant. Only difference between GC owned data and stack allocated one is that former can have scoped view optionally but for the latter compiler must force it as the only available.
Dec 15 2014
On Monday, 15 December 2014 at 11:32:11 UTC, Dicebot wrote:On Monday, 15 December 2014 at 11:23:28 UTC, Marc Schütz wrote:I see this as the same thing, conceptually (module the "transitive" in in your parentheses). It's just a different mechanism. Scope members are just a short-cut instead of writing an accessor method that returns scope.I think there are two cases that are relevant. 1) the tree nodes don't own their children, instead they are managed by - let's say - a region allocator, and 2) the tree nodes do own their children. In both cases, they can declare the children as scope members (assuming a proposal that allows that). The only case I can think of where the children should not be scope members is when they are GC managed. But in this case, you don't need to worry about references to them escaping. Therefore, I don't see a use case for transitivity of scope, from a conceptional point of view. If a particular design makes transitivity necessary, this points to a flaw in the design, IMO.Actually I think in neither case children should be declared as scope members. Instead those should be declared as private members but methods that return pointers/references to children would be declared to return (transitive) scope pointers/references.For me "scopeness" is a property of "view", not object itself - this also makes ownership method of actual data irrelevant.Yes, and this would be true for scope members just the same. (If the aggregate "owns" it's scope members, it will however get in trouble when it tries to free() them, therefore your mechanism is more appropriate in this case.)Only difference between GC owned data and stack allocated one is that former can have scoped view optionally but for the latter compiler must force it as the only available.Agreed.
Dec 15 2014
On Monday, 15 December 2014 at 11:32:11 UTC, Dicebot wrote:For me "scopeness" is a property of "view", not object itself - this also makes ownership method of actual data irrelevant. Only difference between GC owned data and stack allocated one is that former can have scoped view optionally but for the latter compiler must force it as the only available.Ha finally something start to make sense here.
Dec 15 2014
On 12/15/2014 5:38 AM, deadalnix wrote:On Monday, 15 December 2014 at 11:32:11 UTC, Dicebot wrote:Well, the DIP does defined scope in terms of a 'view' in just this manner. I am obviously terrible at explaining things.For me "scopeness" is a property of "view", not object itself - this also makes ownership method of actual data irrelevant. Only difference between GC owned data and stack allocated one is that former can have scoped view optionally but for the latter compiler must force it as the only available.Ha finally something start to make sense here.
Dec 15 2014
On Tuesday, 16 December 2014 at 01:00:47 UTC, Walter Bright wrote:On 12/15/2014 5:38 AM, deadalnix wrote:No, I actually think the concept of view in the proposal is a good one. However, the proposal itself is too limiting.On Monday, 15 December 2014 at 11:32:11 UTC, Dicebot wrote:Well, the DIP does defined scope in terms of a 'view' in just this manner. I am obviously terrible at explaining things.For me "scopeness" is a property of "view", not object itself - this also makes ownership method of actual data irrelevant. Only difference between GC owned data and stack allocated one is that former can have scoped view optionally but for the latter compiler must force it as the only available.Ha finally something start to make sense here.
Dec 16 2014
On Tuesday, 16 December 2014 at 01:00:47 UTC, Walter Bright wrote:On 12/15/2014 5:38 AM, deadalnix wrote:Don't you think there is a contradiction between this notion of "view" and being so focused on lifetimes? Does it make sense to explicitly throw away lifetime information and then spend a lot of effort on preventing lifetime-violating assignments to take place? The current proposal is either too limiting or not limiting enough. Too limiting, because programmers expect to gain something for making parameter types explicit. A naive "make no assumptions view" is more suitable as a default, then let the programmer specify the specifics where possible/needed, but you need to figure out the specifics first and let the "make no assumptions view default" be the leftovers… The proposal is not limiting enough, because retaining knowledge about allocation order and aliasing brings important benefits: An ordered "linear" parameter type would have properties such as: - being aliasfree - being on the stack, and cheaply checkable against the stack pointer Sure, ref counting is fairly general for dynamic tracking, but it is also quite expensive. A check against the stack-pointer is much cheaper. I think the uncanny valley metaphor that was brought up was quite good. If you are going to put in constraints on references, then do it in a manner that is easy to deal with and which brings benefits such as aliasfree references. If constraints become "arbitrary" then usability will suffer and it will seem pointless to put in extra work to use them. You have already gone more or less wholesale for templates, so you might as well use it here as well, to bring substantial benefits to stack-allocation, because the current proposal is a very weak in terms of benefits.On Monday, 15 December 2014 at 11:32:11 UTC, Dicebot wrote:Well, the DIP does defined scope in terms of a 'view' in just this manner. I am obviously terrible at explaining things.For me "scopeness" is a property of "view", not object itself - this also makes ownership method of actual data irrelevant. Only difference between GC owned data and stack allocated one is that former can have scoped view optionally but for the latter compiler must force it as the only available.Ha finally something start to make sense here.
Dec 16 2014
On 12/16/2014 5:37 AM, "Ola Fosheim Grøstad" <ola.fosheim.grostad+dlang gmail.com>" wrote:The current proposal is either too limiting or not limiting enough.I'm afraid I don't understand at all what you wrote.
Dec 16 2014
Walter Bright:I'm afraid I don't understand at all what you wrote.Perhaps reading about linear type systems could help: http://en.wikipedia.org/w/index.php?title=Substructural_type_system&redirect=no#Linear_type_systems Bye, bearophile
Dec 16 2014
On Wednesday, 17 December 2014 at 07:48:52 UTC, bearophile wrote:Walter Bright:But note that we want a "relaxed" linear type system. In general, we are fine with multiple aliases, although there are some applications where strict uniqueness is useful. Deadalnix' proposal goes in this direction.I'm afraid I don't understand at all what you wrote.Perhaps reading about linear type systems could help: http://en.wikipedia.org/w/index.php?title=Substructural_type_system&redirect=no#Linear_type_systems
Dec 17 2014
On Wednesday, 17 December 2014 at 11:13:02 UTC, Marc Schütz wrote:On Wednesday, 17 December 2014 at 07:48:52 UTC, bearophile wrote:Pointer aliasing is an optimization killer, which is why C99 added the «restrict» keyword. I think in most cases the use of stack allocated objects tend to be alias-free, so it is important that this knowledge is retained so that the compiler can make use of it. http://en.wikipedia.org/wiki/Pointer_aliasingWalter Bright:But note that we want a "relaxed" linear type system. In general, we are fine with multiple aliases, although there areI'm afraid I don't understand at all what you wrote.Perhaps reading about linear type systems could help: http://en.wikipedia.org/w/index.php?title=Substructural_type_system&redirect=no#Linear_type_systems
Dec 17 2014
On Wednesday, 17 December 2014 at 12:29:21 UTC, Ola Fosheim Grøstad wrote:On Wednesday, 17 December 2014 at 11:13:02 UTC, Marc Schütz wrote:I know, this is one of those applications. The one I was thinking of is const-borrowing (useful to prevent iterator invalidation and a bunch of other things). I think that enforcing alias-freeness by default (the way a strict linear type system requires) doesn't fit too well into the existing language. Some way of signalling this to the compiler will therefore be required. It might be as simple as C's `restrict` keyword (but it needs to be enforced!), or better yet, an `unrestrict` keyword. Parameters would be alias-free by default unless explicitly specified otherwise. Local aliases (inside one function) are however ok, as long as not more than one is passed to another function at the same time.On Wednesday, 17 December 2014 at 07:48:52 UTC, bearophile wrote:Pointer aliasing is an optimization killer, which is why C99 added the «restrict» keyword. I think in most cases the use of stack allocated objects tend to be alias-free, so it is important that this knowledge is retained so that the compiler can make use of it. http://en.wikipedia.org/wiki/Pointer_aliasingWalter Bright:But note that we want a "relaxed" linear type system. In general, we are fine with multiple aliases, although there areI'm afraid I don't understand at all what you wrote.Perhaps reading about linear type systems could help: http://en.wikipedia.org/w/index.php?title=Substructural_type_system&redirect=no#Linear_type_systems
Dec 17 2014
On Wednesday, 17 December 2014 at 13:35:53 UTC, Marc Schütz wrote:I think that enforcing alias-freeness by default (the way a strict linear type system requires) doesn't fit too well into the existing language. Some way of signalling this to the compiler will therefore be required. It might be as simple as C's `restrict` keyword (but it needs to be enforced!), or better yet, an `unrestrict` keyword. Parameters would be alias-free by default unless explicitly specified otherwise. Local aliases (inside one function) are however ok, as long as not more than one is passed to another function at the same time.Hmm, I kind of feel that D is having too many parameter keywords/types/qualifiers. Parameter specification is primarily useful where it provides information about constraints that the programmer need to know about when using a library. "restricted scope ref" could be a useful default for non-templated functions, then use templated ownership reference typing or inference to go beyond it. I think that if one improved the templating system so that it was capable of analysing similarities between instantiations and use heuristics to analyse when a new instance is profitable then you could make functions templated by default and propagate knowledge from the call site to the template-instantiation mechanism of the compiler. So if the reference is known to be aliasfree then the compiler can use heuristics to determine if it should instantiate a "restricted" version or use a slower general version of the templated function. Similarily, if refcounters always are on offset 0 and destructors are virtual, then you only need a single container method template instance for refcouting references, even if you have many types using the same method. If refcounters are big enough, you can have a unique_ptr that just set it to a large value and use the same template instance for a unique_ptr and refcounted_ptr behind the scene when referencing objects that provide a refcount field. There are also many other things that could be done to enhance the template system, like querying properties of the block in loops that iterate over a "range", so that you can instantiate different "optimized ranges" depending on whether the loop contains a "break" statement or not. Or like Jonathan Blow's language, where you can issue a "remove element and continue" like statement within a foreach-loop, which might require a different "range iteration" implementation.
Dec 17 2014
On Wednesday, 17 December 2014 at 12:29:21 UTC, Ola Fosheim Grøstad wrote:On Wednesday, 17 December 2014 at 11:13:02 UTC, Marc Schütz wrote:If you have owner ship, you have free. If you have a pair alloc/free then you can promote on stack. This is a much more powerful way to handle things, as this take advantage of inlining.On Wednesday, 17 December 2014 at 07:48:52 UTC, bearophile wrote:Pointer aliasing is an optimization killer, which is why C99 added the «restrict» keyword. I think in most cases the use of stack allocated objects tend to be alias-free, so it is important that this knowledge is retained so that the compiler can make use of it. http://en.wikipedia.org/wiki/Pointer_aliasingWalter Bright:But note that we want a "relaxed" linear type system. In general, we are fine with multiple aliases, although there areI'm afraid I don't understand at all what you wrote.Perhaps reading about linear type systems could help: http://en.wikipedia.org/w/index.php?title=Substructural_type_system&redirect=no#Linear_type_systems
Dec 17 2014
On Wednesday, 17 December 2014 at 13:48:24 UTC, deadalnix wrote:If you have owner ship, you have free. If you have a pair alloc/free then you can promote on stack. This is a much more powerful way to handle things, as this take advantage of inlining.More powerful than what? Rather than thinking in terms of alloc/free it might be helpful to think about it more generally as resources, use, reuse and side-effects. On the stack you get to reuse memory when the variable is not longer live, meaning you junk writes if the results cannot be read and reuse the memory "prematurely". http://en.wikipedia.org/wiki/Live_variable_analysis If you are saying that turning GC allocations automatically into stack allocations is a good direction for D2, then I agree.
Dec 17 2014
On Wednesday, 17 December 2014 at 05:28:35 UTC, Walter Bright wrote:On 12/16/2014 5:37 AM, "Ola Fosheim Grøstad" <ola.fosheim.grostad+dlang gmail.com>" wrote:Well, look at it this way: D1 took 1990s style C++ application programming model and made it more convenient with a GC. Having a GC-memory model with the possibility of stack/heap allocations as optimizations can keep the language simple. D2 is more of a library author's language, but without GC you need to deal with different types of ownership. If you toss out the GC, and want the same level of safety and generality you either: 1. need to track ownership add dynamic runtime checks 2. provide a full blown proof system (not realistic) 3. provide means for the programmer to "help" the semantic analysis What you want is a combination of 1 and 3, but you don't need to keep them separate. Since refcounting is expensive, you don't want that everywhere. For stack allocated objects you have an implicit "refcounting" in the nesting of scopes, you only need to track scope-depth. If the compiler knows that a returned object is stack allocated, then it can check that by comparing against the stack frame address since the stack grows downwards in an ordered fashion. And in most cases it should be able to elide the check. If you also maintain information about order (stack depth) among the stack allocated objects a function receives through reference parameters (for every function call), then you can safely combine the stack allocated objects you receive through parameters in aggregates, like inserting an stack allocated element into a container. Since D is increasingly becoming a library author's language, you probably also would benefit more from strengthening the template support by having a templated ref-type rather than "scope ref". You could define protocols (e.g. predefined UDAs) that tells the compiler what kind of key properties the library type provides, such as ownership. Then use that for a more generic approach to optimization and escape analysis and let the ownership-reference-types be library types. If semantic analysis gets rid of the dynamic aspects of a referencs (such as reference counting) then the compiler should be able to use the same function body, and in other cases just add a inferred wrapper to a generic function body. So the code bloat can be limited. If all references to external resources (like the heap) in the stack allocated object is owned by the object, then you can treat those as a whole. But you need the compiler to understand what ownership is if it is to guarantee memory safety in the kind of situations where memory safety is known to be difficult to achieve. The alternative is to state that safe is not covering non-GC memory and that one should stick to vetted library code when writing safe code that does not use the GC. I actually find that acceptable for D2. In D2 you would be better off spending the effort at finding ways to turn automatically turn GC allocations into stack allocations as optimizations. Then you can find a more general and powerful ownership-oriented approach for non-GC applications for a new major version of D. It is not realistic that people will annotate with "scope ref", and compiler inference could make updates to libraries break application code if you get false negatives. Function signatures are contracts, they ought to be explicit for libraries. So having library functions act as "scope ref" without it being explicit in the signature is not necessarily a good idea.The current proposal is either too limiting or not limiting enough.I'm afraid I don't understand at all what you wrote.
Dec 17 2014
On Tuesday, 16 December 2014 at 01:00:47 UTC, Walter Bright wrote:On 12/15/2014 5:38 AM, deadalnix wrote:Such notion of "view" requires at least some elements of transitivity to be practical in my opinion. Also with my definition in mind your example of tree that stores scope nodes makes absolutely no sense unless whole tree itself is scoped (and nodes are thus scoped transitively). Such view is always assumes worst case about ownership and shouldn't persist in any form (as that would require some serious ownership tracking). I don't think you have explained your case that bad - we simply have a very different use cases in mind as "primary" use case.On Monday, 15 December 2014 at 11:32:11 UTC, Dicebot wrote:Well, the DIP does defined scope in terms of a 'view' in just this manner. I am obviously terrible at explaining things.For me "scopeness" is a property of "view", not object itself - this also makes ownership method of actual data irrelevant. Only difference between GC owned data and stack allocated one is that former can have scoped view optionally but for the latter compiler must force it as the only available.Ha finally something start to make sense here.
Dec 19 2014
On 12/19/2014 9:44 PM, Dicebot wrote:Such notion of "view" requires at least some elements of transitivity to be practical in my opinion.I have no idea how "some elements of transitivity" can even work. It's either transitive or its not. Please don't think of scope in terms of ownership, ownership is an orthogonal issue.Also with my definition in mind your example of tree that stores scope nodes makes absolutely no sense unless whole tree itself is scoped (and nodes are thus scoped transitively). Such view is always assumes worst case about ownership and shouldn't persist in any form (as that would require some serious ownership tracking).This is definitely conflating scope and ownership.
Dec 20 2014
On Saturday, 20 December 2014 at 21:39:44 UTC, Walter Bright wrote:On 12/19/2014 9:44 PM, Dicebot wrote:.. and here I was about you to do exactly the same :P What in example I show makes you think of ownership? When I was speaking about "some elements of transitivity" I was thinking in a way of keeping scope storage class but transitively applying same restrictions to all data accessible through it AS IF it had scope storage class on its own - while still making illegal to use scope as a separate type qualifier.Such notion of "view" requires at least some elements of transitivity to be practical in my opinion.I have no idea how "some elements of transitivity" can even work. It's either transitive or its not. Please don't think of scope in terms of ownership, ownership is an orthogonal issue.No, it is exactly the other way around. The very point of what I am saying is that you DOESN'T CARE about ownership as long as worst case scenario is assumed. I have zero idea why you identify it is conflating as ownership when it is explicitly designed to be distinct.Also with my definition in mind your example of tree that stores scope nodes makes absolutely no sense unless whole tree itself is scoped (and nodes are thus scoped transitively). Such view is always assumes worst case about ownership and shouldn't persist in any form (as that would require some serious ownership tracking).This is definitely conflating scope and ownership.
Dec 21 2014
On 12/21/2014 2:06 AM, Dicebot wrote:No, it is exactly the other way around. The very point of what I am saying is that you DOESN'T CARE about ownership as long as worst case scenario is assumed. I have zero idea why you identify it is conflating as ownership when it is explicitly designed to be distinct.The point of transitive scoping would be if the root owned the data reachable through the root.
Dec 21 2014
On Monday, 22 December 2014 at 03:07:53 UTC, Walter Bright wrote:On 12/21/2014 2:06 AM, Dicebot wrote:Quoting myself:No, it is exactly the other way around. The very point of what I am saying is that you DOESN'T CARE about ownership as long as worst case scenario is assumed. I have zero idea why you identify it is conflating as ownership when it is explicitly designed to be distinct.The point of transitive scoping would be if the root owned the data reachable through the root.For me "scopeness" is a property of "view", not object itself - this also makes ownership method of actual data irrelevant. Only difference between GC owned data and stack allocated one is that former can have scoped view optionally but for the latter compiler must force it as the only available.It doesn't matter of root owns the data. We _assume_ that as worst case scenario and allowed actions form a strict subset of allowed actions for any other ownership situation. Such `scope` for stack/GC is same as `const` for mutable/immutable - common denominator. Point of transitive scope is to make easy to expose complex custom data structures without breaking memory safety.
Dec 22 2014
On 12/22/2014 12:04 AM, Dicebot wrote:Point of transitive scope is to make easy to expose complex custom data structures without breaking memory safety.I do understand that. Making it work with the type system is another matter entirely - it's far more complex than just adding a qualifier. 'inout' looks simple but still has ongoing issues. And the thing is, wrappers can be used instead of qualifiers, in the same places in the same way. It's much simpler.
Dec 22 2014
On Monday, 22 December 2014 at 20:51:46 UTC, Walter Bright wrote:On 12/22/2014 12:04 AM, Dicebot wrote:Ok, lets consider some examples of code I'd want to work with wrapper approach. Basic tree structure: struct Node { Node* left, right; Payload payload; } struct Tree { Node* root; } Restricting public access to nodes to prevent storing persistent node pointers. Your proposed approach: struct Wrapper(T) { scope T wrapped; alias wrapped this; } struct Node { private: Node* _left, _right; public: auto left() property { return Wrapper!(Node*)(_left); } auto right() property { return Wrapper!(Node*)(_right); } Payload payload; } struct Tree { private: Node* _root; public: auto root() property { return Wrapper!(Node*)(_root); } } Am I correct? If yes, this is exactly the breakage issue I was speaking about : any code that looked like `is(typeof(Tree.root) == Node*)` will stop working. With my latest proposal (pseudo-transitive storage class which is not part of a type) getters would look simply like `scope Node* root() property { return _root; }` and `is(typeof(Tree.root) == Node*)` still remained true. Only impact on existing would be restriction of operations to take address or assign a pointer. I am pretty sure I am missing many complicated parts here but this seems like a big deal in retro-fitting existing libraries to use scope after it was added.Point of transitive scope is to make easy to expose complex custom data structures without breaking memory safety.I do understand that. Making it work with the type system is another matter entirely - it's far more complex than just adding a qualifier. 'inout' looks simple but still has ongoing issues. And the thing is, wrappers can be used instead of qualifiers, in the same places in the same way. It's much simpler.
Dec 29 2014
On Friday, 12 December 2014 at 00:13:10 UTC, deadalnix wrote:On Thursday, 11 December 2014 at 13:55:55 UTC, Marc Schütz wrote:Steven hadn't, evidently.This is a point that most people don't seem to understand yet, and which wasn't obvious for me either, at the beginning: * A `ref` parameter means that it cannot escape the function, _except_ by return. * A `scope ref` parameter means that it cannot escape the function _ever_, not even by return. * A `scope ref` return means that it cannot leave the current statement. Therefore, a `scope ref` return value can be passed on to the next function as a `ref` argument. If that function again returns a reference (even if not explicitly designated as `scope`), the compiler will treat it as if it were `scope ref`.No, it understood.It is simply not useful.I'm not convinced either.My comments above don't refer to this proposal, but my original one: struct Wrapper(T) { scope T payload; // used to be: // scope!this T payload; } scope int a; auto w = Wrapper!(int*)(&a); // ok scope int b; w.payload = &b; // error, w (and therefore w.payload) // lives longer than b Wrapper!(int*) w2 = w; // ok, lifetime(w2) < lifetime(w) w = w2; // error, equivalent to // w.payload = w2.payload Therefore, wrapping is safe without transitivity of scope.I agree, this is important. In my proposal, this works without transitivity. The wrapper stores the pointer as a `scope` member, then by copying the wrapper, the pointer gets copied implicitly, to which the normal scope restrictions apply (`scope` on members means "will not outlive the aggregate"). If it stored it as normal non-scope pointer, it couldn't get assigned in the first place.Wut ? You cante store anything with grear lifetime, including non scope things (as they'll have infinite lifetime). Meaning the only thing you know, is that thing are possibly scoped. Meaning you have to assume infinite lifetime with every indirection, which make this proposal useless.
Dec 12 2014
On 12/11/2014 4:13 PM, deadalnix wrote:On Thursday, 11 December 2014 at 13:55:55 UTC, Marc Schütz wrote:The use case is described in the DIP under the 'Scope Ref' heading.On Thursday, 11 December 2014 at 12:48:05 UTC, Manu via Digitalmars-d wrote:No, it understood. It is simply not useful.On 8 December 2014 at 07:29, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:This is a point that most people don't seem to understand yet, and which wasn't obvious for me either, at the beginning: * A `ref` parameter means that it cannot escape the function, _except_ by return. * A `scope ref` parameter means that it cannot escape the function _ever_, not even by return. * A `scope ref` return means that it cannot leave the current statement. Therefore, a `scope ref` return value can be passed on to the next function as a `ref` argument. If that function again returns a reference (even if not explicitly designated as `scope`), the compiler will treat it as if it were `scope ref`.On 12/7/2014 6:12 AM, Dicebot wrote:Will it? It looks like foo can't be called with scope data?But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.It means you'll have to wrap the next level of indirection in a ref counting (or equivalent) manner. The great thing about GC is you can leave everything to the GC. But when explicitly managing memory, you have to decide for EVERY pointer who owns it, when it can be released, etc. Having a transitive scope is NOT going to resolve that for you, it isn't even going to help.I agree, this is important. In my proposal, this works without transitivity. The wrapper stores the pointer as a `scope` member, then by copying the wrapper, the pointer gets copied implicitly, to which the normal scope restrictions apply (`scope` on members means "will not outlive the aggregate"). If it stored it as normal non-scope pointer, it couldn't get assigned in the first place.Wut ? You cante store anything with grear lifetime, including non scope things (as they'll have infinite lifetime). Meaning the only thing you know, is that thing are possibly scoped. Meaning you have to assume infinite lifetime with every indirection, which make this proposal useless.
Dec 12 2014
On 11 December 2014 at 23:55, via Digitalmars-d <digitalmars-d puremagic.com> wrote:On Thursday, 11 December 2014 at 12:48:05 UTC, Manu via Digitalmars-d wrote:Ummm. I reckon there's a good reason that people don't seem to understand this... because it would have difficulty being any more unintuitive! It's contrary to the behaviour of basically everything else in D! const, pure, nothrow, etc... they all work a reliable and consistent way. Also, I still don't think I get it... a scope-ref return, which has a restriction as you say, can be subverted by being piped through a function that receives and returns it as ref? What is the point?On 8 December 2014 at 07:29, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:This is a point that most people don't seem to understand yet, and which wasn't obvious for me either, at the beginning: * A `ref` parameter means that it cannot escape the function, _except_ by return. * A `scope ref` parameter means that it cannot escape the function _ever_, not even by return. * A `scope ref` return means that it cannot leave the current statement. Therefore, a `scope ref` return value can be passed on to the next function as a `ref` argument. If that function again returns a reference (even if not explicitly designated as `scope`), the compiler will treat it as if it were `scope ref`.On 12/7/2014 6:12 AM, Dicebot wrote:Will it? It looks like foo can't be called with scope data?But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.How can a member be marked scope? Apparently it's a storage class, not a type constructor... we can only attribute storage classes to function args/results(?).I'm also quite uneasy with the fact that scope would not be transitive as a storage class. What happens when it's applied to a value type, like a struct, that contains some pointers? An adaptation wrapper for a single pointer is super common; ie, a tiny struct passed by value with scope needs to have it's contained pointer receive the scope-ness of the argument.I agree, this is important. In my proposal, this works without transitivity. The wrapper stores the pointer as a `scope` member, then by copying the wrapper, the pointer gets copied implicitly, to which the normal scope restrictions apply (`scope` on members means "will not outlive the aggregate"). If it stored it as normal non-scope pointer, it couldn't get assigned in the first place. (Additionally, some higher level tricks are possible if we allow scope for value types and overloading on scope.)
Dec 12 2014
On Friday, 12 December 2014 at 08:42:22 UTC, Manu via Digitalmars-d wrote:On 11 December 2014 at 23:55, via Digitalmars-d <digitalmars-d puremagic.com> wrote:Unfortunately I have to agree.This is a point that most people don't seem to understand yet, and which wasn't obvious for me either, at the beginning: * A `ref` parameter means that it cannot escape the function, _except_ by return. * A `scope ref` parameter means that it cannot escape the function _ever_, not even by return. * A `scope ref` return means that it cannot leave the current statement. Therefore, a `scope ref` return value can be passed on to the next function as a `ref` argument. If that function again returns a reference (even if not explicitly designated as `scope`), the compiler will treat it as if it were `scope ref`.Ummm. I reckon there's a good reason that people don't seem to understand this... because it would have difficulty being any more unintuitive! It's contrary to the behaviour of basically everything else in D! const, pure, nothrow, etc... they all work a reliable and consistent way.Also, I still don't think I get it... a scope-ref return, which has a restriction as you say, can be subverted by being piped through a function that receives and returns it as ref? What is the point?It cannot be subverted. As soon as you pass something into a function G() as `ref` that's been returned from a function F() via `scope ref`, the compiler needs to treat what is returned from G() as `scope ref`, even if it's only declared as `ref`. As you say, it's unintuitive and non-obvious.I was talking about my proposal here, where it's a type modifier, not a storage class. deadalnix also brought up the problem of transitivity. This, too, would simply go away if it's a type modifier.How can a member be marked scope? Apparently it's a storage class, not a type constructor... we can only attribute storage classes to function args/results(?).I'm also quite uneasy with the fact that scope would not be transitive as a storage class. What happens when it's applied to a value type, like a struct, that contains some pointers? An adaptation wrapper for a single pointer is super common; ie, a tiny struct passed by value with scope needs to have it's contained pointer receive the scope-ness of the argument.I agree, this is important. In my proposal, this works without transitivity. The wrapper stores the pointer as a `scope` member, then by copying the wrapper, the pointer gets copied implicitly, to which the normal scope restrictions apply (`scope` on members means "will not outlive the aggregate"). If it stored it as normal non-scope pointer, it couldn't get assigned in the first place. (Additionally, some higher level tricks are possible if we allow scope for value types and overloading on scope.)
Dec 12 2014
On 12 December 2014 at 22:18, via Digitalmars-d <digitalmars-d puremagic.com> wrote:On Friday, 12 December 2014 at 08:42:22 UTC, Manu via Digitalmars-d wrote:Oh okay. Is that still on the table?How can a member be marked scope? Apparently it's a storage class, not a type constructor... we can only attribute storage classes to function args/results(?).I was talking about my proposal here, where it's a type modifier, not a storage class.deadalnix also brought up the problem of transitivity. This, too, would simply go away if it's a type modifier.Just for clarity, does type modifier mean the same thing as type constructor?
Dec 12 2014
On Friday, 12 December 2014 at 12:34:40 UTC, Manu via Digitalmars-d wrote:On 12 December 2014 at 22:18, via Digitalmars-d <digitalmars-d puremagic.com> wrote:Dunno, maybe? DIP69 doesn't look very well-received at the moment...On Friday, 12 December 2014 at 08:42:22 UTC, Manu via Digitalmars-d wrote:Oh okay. Is that still on the table?How can a member be marked scope? Apparently it's a storage class, not a type constructor... we can only attribute storage classes to function args/results(?).I was talking about my proposal here, where it's a type modifier, not a storage class.Yes. "Type modifier" = const, shared, immutable, etc.; "type constructor" = the syntactical construct by which a type is constructed/modified. Andrei suggested this differentiation some time ago (sorry, don't have a reference at hand).deadalnix also brought up the problem of transitivity. This, too, would simply go away if it's a type modifier.Just for clarity, does type modifier mean the same thing as type constructor?
Dec 12 2014
On Friday, 12 December 2014 at 13:38:10 UTC, Marc Schütz wrote:Yes. "Type modifier" = const, shared, immutable, etc.; "type constructor" = the syntactical construct by which a type is constructed/modified. Andrei suggested this differentiation some time ago (sorry, don't have a reference at hand).That won't fly. scope turtle left, type qualifier turtle right.
Dec 12 2014
On Friday, 12 December 2014 at 12:34:40 UTC, Manu via Digitalmars-d wrote:It appears to me that scopeness is a completely different beast than type qualifier, and type qualifier won't work. I think type modifier was just made up here. And why not ? It is more like some metadata (lifetime) being associated with value and symbols, like a type would, but that isn't really per se a type as D understands it right now.deadalnix also brought up the problem of transitivity. This, too, would simply go away if it's a type modifier.Just for clarity, does type modifier mean the same thing as type constructor?
Dec 12 2014
On 12/11/2014 4:47 AM, Manu via Digitalmars-d wrote:On 8 December 2014 at 07:29, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:Yes, it can be.On 12/7/2014 6:12 AM, Dicebot wrote:Will it? It looks like foo can't be called with scope data?But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.I don't have the perfect proposal, but I feel very strongly about 2 things: 1. It must not be a storage class; the concept was a disaster with ref, and I struggle with this more frequently than any other 'feature' in D.I simply do not understand why distinguishing beteen ref and not-ref is a cornerstone of everything you do.2. I feel it's a big mistake to separate it from the type system, which I think most agree, is D's greatest asset by far. Manipulating types is simple and convenient using the type system, and I think manipulating scope will be just as important as any other attribute as soon as even slightly complex use cases begin to arise.Consider a ref counted type, RC!T. If scope were transitive, then you could not have, say, a tree where the edges were RC!T. I.e., the payload of an RC type should not be forced to be scope.
Dec 11 2014
12-Dec-2014 00:41, Walter Bright пишет:On 12/11/2014 4:47 AM, Manu via Digitalmars-d wrote:On 8 December 2014 at 07:29, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:I would hazard a guess that const T& is something lots of C++ got addicted to as the most common way of parameter passing. There is nothing exactly like that in D, auto ref was quite close but only for templates and it instantiates 2 distinct bodies for ref and non-ref at the moment. It allows easy pass by ref for both r-values and l-values with logical const and is likely what Manu is referring(!) to. Truth be told it's not always the fastest way, in fact Boost C++ has template for optimal parameter passing - call_traits<T>::param_type but it must be too cumbersome for actual use as I never seen it used in the wild. http://www.boost.org/doc/libs/1_57_0/libs/utility/call_traits.htm -- Dmitry OlshanskyI don't have the perfect proposal, but I feel very strongly about 2 things: 1. It must not be a storage class; the concept was a disaster with ref, and I struggle with this more frequently than any other 'feature' in D.I simply do not understand why distinguishing beteen ref and not-ref is a cornerstone of everything you do.
Dec 11 2014
On Thursday, 11 December 2014 at 21:41:11 UTC, Walter Bright wrote:On 12/11/2014 4:47 AM, Manu via Digitalmars-d wrote:Because he requires control over function ABIs for both inter-language communication and performance. In binding D to IDL (Interactive Data, not Interface Description) I found ref often required special casing.On 8 December 2014 at 07:29, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:Yes, it can be.On 12/7/2014 6:12 AM, Dicebot wrote:Will it? It looks like foo can't be called with scope data?But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.I don't have the perfect proposal, but I feel very strongly about 2 things: 1. It must not be a storage class; the concept was a disaster with ref, and I struggle with this more frequently than any other 'feature' in D.I simply do not understand why distinguishing beteen ref and not-ref is a cornerstone of everything you do.
Dec 11 2014
On 12 December 2014 at 08:53, John Colvin via Digitalmars-d <digitalmars-d puremagic.com> wrote:On Thursday, 11 December 2014 at 21:41:11 UTC, Walter Bright wrote:Thank you. I feel like I'm just repeating myself over and over again.On 12/11/2014 4:47 AM, Manu via Digitalmars-d wrote:Because he requires control over function ABIs for both inter-language communication and performance. In binding D to IDL (Interactive Data, not Interface Description) I found ref often required special casing.On 8 December 2014 at 07:29, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:Yes, it can be.On 12/7/2014 6:12 AM, Dicebot wrote:Will it? It looks like foo can't be called with scope data?But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.I don't have the perfect proposal, but I feel very strongly about 2 things: 1. It must not be a storage class; the concept was a disaster with ref, and I struggle with this more frequently than any other 'feature' in D.I simply do not understand why distinguishing beteen ref and not-ref is a cornerstone of everything you do.
Dec 12 2014
On 12/12/2014 4:20 AM, Manu via Digitalmars-d wrote:On 12 December 2014 at 08:53, John Colvin via Digitalmars-dExample, please.Because he requires control over function ABIs for both inter-language communication and performance. In binding D to IDL (Interactive Data, not Interface Description) I found ref often required special casing.Thank you. I feel like I'm just repeating myself over and over again.
Dec 12 2014
On Thursday, 11 December 2014 at 21:41:11 UTC, Walter Bright wrote:Consider a ref counted type, RC!T. If scope were transitive, then you could not have, say, a tree where the edges were RC!T. I.e., the payload of an RC type should not be forced to be scope.I don't see how this is related. It would be perfectly ok to declare root of such tree scope if it was transitive (as long as it only controls access and does not attempt early destruction). You may want to explain this in details in DIP if this is motivating case.
Dec 11 2014
On Friday, 12 December 2014 at 06:06:40 UTC, Dicebot wrote:On Thursday, 11 December 2014 at 21:41:11 UTC, Walter Bright wrote:That is my proposal. However, it is not as simply as you think it is without making it a type qualifier (which is not desirable). In effect, that mean that you can see something that has infinite lifetime through a scope reference, so you need to track lifetime rvalue and lvalue differently. I'm still for it. The current proposal is not powerful enough to pull its weight.Consider a ref counted type, RC!T. If scope were transitive, then you could not have, say, a tree where the edges were RC!T. I.e., the payload of an RC type should not be forced to be scope.I don't see how this is related. It would be perfectly ok to declare root of such tree scope if it was transitive (as long as it only controls access and does not attempt early destruction).
Dec 11 2014
On Friday, 12 December 2014 at 06:57:54 UTC, deadalnix wrote:On Friday, 12 December 2014 at 06:06:40 UTC, Dicebot wrote:I don't hope it will be simple. It is all about making scope as simple as possible to keep it useful for idiomatic D code - but not simpler.On Thursday, 11 December 2014 at 21:41:11 UTC, Walter Bright wrote:That is my proposal. However, it is not as simply as you think it is without making it a type qualifier (which is not desirable). In effect, that mean that you can see something that has infinite lifetime through a scope reference, so you need to track lifetime rvalue and lvalue differently. I'm still for it. The current proposal is not powerful enough to pull its weight.Consider a ref counted type, RC!T. If scope were transitive, then you could not have, say, a tree where the edges were RC!T. I.e., the payload of an RC type should not be forced to be scope.I don't see how this is related. It would be perfectly ok to declare root of such tree scope if it was transitive (as long as it only controls access and does not attempt early destruction).
Dec 11 2014
On 12/11/2014 10:06 PM, Dicebot wrote:On Thursday, 11 December 2014 at 21:41:11 UTC, Walter Bright wrote:Are you suggesting two kinds of scope - transitive and non-transitive? The more I think about it, the more ref counting is the definitive case, as just about everything else can be explained in terms of how ref counting works.Consider a ref counted type, RC!T. If scope were transitive, then you could not have, say, a tree where the edges were RC!T. I.e., the payload of an RC type should not be forced to be scope.I don't see how this is related. It would be perfectly ok to declare root of such tree scope if it was transitive (as long as it only controls access and does not attempt early destruction).
Dec 11 2014
On Friday, 12 December 2014 at 07:48:21 UTC, Walter Bright wrote:I don't see applicability of non-transitive scope because I don't understand the problem with the tree or reference counting you have mentioned - that is why I suggested to amend DIP to put explanation there.I don't see how this is related. It would be perfectly ok to declare root of such tree scope if it was transitive (as long as it only controls access and does not attempt early destruction).Are you suggesting two kinds of scope - transitive and non-transitive? The more I think about it, the more ref counting is the definitive case, as just about everything else can be explained in terms of how ref counting works.
Dec 11 2014
On 12/11/2014 11:55 PM, Dicebot wrote:On Friday, 12 December 2014 at 07:48:21 UTC, Walter Bright wrote:Consider every pointer in a tree to be a ref counted pointer. There is no purpose to making scope transitive - traveling from one node to the next goes through the usual ref counting mechanism, and the ref counting wrapper can control that. Also, consider a data structure: A -> B C -> B If scope is transitive, how is C going to simultaneously access B?I don't see applicability of non-transitive scope because I don't understand the problem with the tree or reference counting you have mentioned - that is why I suggested to amend DIP to put explanation there.I don't see how this is related. It would be perfectly ok to declare root of such tree scope if it was transitive (as long as it only controls access and does not attempt early destruction).Are you suggesting two kinds of scope - transitive and non-transitive? The more I think about it, the more ref counting is the definitive case, as just about everything else can be explained in terms of how ref counting works.
Dec 12 2014
On Friday, 12 December 2014 at 07:48:21 UTC, Walter Bright wrote:Are you suggesting two kinds of scope - transitive and non-transitive?Non transitive scope can be added without any language extension. There is no point doing much with the scope keyword if indirection aren't in the loop.The more I think about it, the more ref counting is the definitive case, as just about everything else can be explained in terms of how ref counting works.ref counting is just one form of ownership.
Dec 12 2014
On 12/12/2014 12:16 AM, deadalnix wrote:On Friday, 12 December 2014 at 07:48:21 UTC, Walter Bright wrote:In order for it to work, the holes in the language that enabled escapes had to be plugged. That's what this proposal does - plug the holes.Are you suggesting two kinds of scope - transitive and non-transitive?Non transitive scope can be added without any language extension.ref counting is just one form of ownership.My point is if that works with this proposal, then the other forms can work, too.
Dec 12 2014
On Friday, 12 December 2014 at 08:44:56 UTC, Walter Bright wrote:On 12/12/2014 12:16 AM, deadalnix wrote:The holes covered by a non transitive scope are already undefined behavior.On Friday, 12 December 2014 at 07:48:21 UTC, Walter Bright wrote:In order for it to work, the holes in the language that enabled escapes had to be plugged. That's what this proposal does - plug the holes.Are you suggesting two kinds of scope - transitive and non-transitive?Non transitive scope can be added without any language extension.No, unless we wrap every single indirection (pointer, slice, classes, delegates) into a wrapper. That is not going to fly very far.ref counting is just one form of ownership.My point is if that works with this proposal, then the other forms can work, too.
Dec 12 2014
On 12/12/2014 2:28 AM, deadalnix wrote:On Friday, 12 December 2014 at 08:44:56 UTC, Walter Bright wrote:Right, but since the current compiler cannot detect them, it is a hole.On 12/12/2014 12:16 AM, deadalnix wrote:The holes covered by a non transitive scope are already undefined behavior.On Friday, 12 December 2014 at 07:48:21 UTC, Walter Bright wrote:In order for it to work, the holes in the language that enabled escapes had to be plugged. That's what this proposal does - plug the holes.Are you suggesting two kinds of scope - transitive and non-transitive?Non transitive scope can be added without any language extension.The beauty of GC is that you don't have to do any of this. To have safe other methods of allocation, there has to be annotation everywhere or use a wrapped type in the same places.No, unless we wrap every single indirection (pointer, slice, classes, delegates) into a wrapper. That is not going to fly very far.ref counting is just one form of ownership.My point is if that works with this proposal, then the other forms can work, too.
Dec 12 2014
On Friday, 12 December 2014 at 22:05:21 UTC, Walter Bright wrote:The whole point of scope is to reduce that cost, both in term of runtime (as copy can be elided) but also on the user.The beauty of GC is that you don't have to do any of this. To have safe other methods of allocation, there has to be annotation everywhere or use a wrapped type in the same places.forms can work, too.No, unless we wrap every single indirection (pointer, slice, classes, delegates) into a wrapper. That is not going to fly very far.
Dec 12 2014
On 12/12/2014 2:09 PM, deadalnix wrote:On Friday, 12 December 2014 at 22:05:21 UTC, Walter Bright wrote:Yes, I do understand that :-) but currently doing a safe refcounted type is impossible in D. This proposal fixes that.The whole point of scope is to reduce that cost, both in term of runtime (as copy can be elided) but also on the user.The beauty of GC is that you don't have to do any of this. To have safe other methods of allocation, there has to be annotation everywhere or use a wrapped type in the same places.forms can work, too.No, unless we wrap every single indirection (pointer, slice, classes, delegates) into a wrapper. That is not going to fly very far.
Dec 12 2014
On Friday, 12 December 2014 at 22:34:27 UTC, Walter Bright wrote:On 12/12/2014 2:09 PM, deadalnix wrote: Yes, I do understand that :-) but currently doing a safe refcounted type is impossible in D. This proposal fixes that.Actually, no it doesn't. It allow to ensure that this is safe, module the RC system is the only owner. This proposal allow for easy to use and fast RC, but certainly do not make it safe.
Dec 12 2014
On 12/12/2014 2:48 PM, deadalnix wrote:On Friday, 12 December 2014 at 22:34:27 UTC, Walter Bright wrote:How not?On 12/12/2014 2:09 PM, deadalnix wrote: Yes, I do understand that :-) but currently doing a safe refcounted type is impossible in D. This proposal fixes that.Actually, no it doesn't. It allow to ensure that this is safe, module the RC system is the only owner. This proposal allow for easy to use and fast RC, but certainly do not make it safe.
Dec 12 2014
On Saturday, 13 December 2014 at 00:06:18 UTC, Walter Bright wrote:How not?scope a = new Stuff(); scope b = a; auto rc1 = RC(a); auto rc2 = RC(b); // Enjoy the free when it comes !
Dec 12 2014
On 12/12/2014 4:07 PM, deadalnix wrote:On Saturday, 13 December 2014 at 00:06:18 UTC, Walter Bright wrote:RC needs to take a type as a parameter, not a symbol.How not?scope a = new Stuff(); scope b = a; auto rc1 = RC(a); auto rc2 = RC(b); // Enjoy the free when it comes !
Dec 12 2014
On 12/12/2014 4:07 PM, deadalnix wrote:On Saturday, 13 December 2014 at 00:06:18 UTC, Walter Bright wrote:What's the type signature of RC ?How not?scope a = new Stuff(); scope b = a; auto rc1 = RC(a); auto rc2 = RC(b); // Enjoy the free when it comes !
Dec 12 2014
On Saturday, 13 December 2014 at 00:52:46 UTC, Walter Bright wrote:On 12/12/2014 4:07 PM, deadalnix wrote:RCWrapper!T RC(T)(scope T t) or somethign along these lines.On Saturday, 13 December 2014 at 00:06:18 UTC, Walter Bright wrote:What's the type signature of RC ?How not?scope a = new Stuff(); scope b = a; auto rc1 = RC(a); auto rc2 = RC(b); // Enjoy the free when it comes !
Dec 12 2014
On 12/12/2014 4:59 PM, deadalnix wrote:On Saturday, 13 December 2014 at 00:52:46 UTC, Walter Bright wrote:And how does RC assign it to the wrapper? (It will fail to compile, and so won't be a safety hole.)On 12/12/2014 4:07 PM, deadalnix wrote:RCWrapper!T RC(T)(scope T t) or somethign along these lines.On Saturday, 13 December 2014 at 00:06:18 UTC, Walter Bright wrote:What's the type signature of RC ?How not?scope a = new Stuff(); scope b = a; auto rc1 = RC(a); auto rc2 = RC(b); // Enjoy the free when it comes !
Dec 12 2014
On 12 December 2014 at 07:41, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 12/11/2014 4:47 AM, Manu via Digitalmars-d wrote:I've said so many times, it's the single greatest regular frustration I encounter, by far. That is of course a relative measure. It's not the 'cornerstone of everything I do'; I don't start discussions about things that are not broken and otherwise painless. It is the thing that is *the most broken*, and leads to the most edge cases, general bloat, and text mixins. It comes up the most frequently, and by that metric alone, I consider it highest priority on my list. I've also said many times before, it possibly stems from the fact that one of the key cornerstones of almost everything I do in D is interact with other languages. This is a practical reality, I can't write all my code in D, and have mountains of existing code to interact with. Boilerplate is the result. D has powerful systems to automate boilerplate, like a carrot dangling right in front of my face, but it's almost always thwarted by ref, and almost exclusively so. My recent work updating LuaD to support all the features I required wasted about 80% of my time dealing with ref issues. In my past C++ bindings solutions, much code, which was otherwise simple, readable, and elegant, turned into a mess of text mixins, almost exclusively because ref is broken.On 8 December 2014 at 07:29, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:Yes, it can be.On 12/7/2014 6:12 AM, Dicebot wrote:Will it? It looks like foo can't be called with scope data?But from existing cases it doesn't seem working good enough. For example, not being able to represent idiom of `scope ref int foo(scope ref int x) { return x; }` seems very limiting.scope ref int foo(ref int x); will do it.I don't have the perfect proposal, but I feel very strongly about 2 things: 1. It must not be a storage class; the concept was a disaster with ref, and I struggle with this more frequently than any other 'feature' in D.I simply do not understand why distinguishing beteen ref and not-ref is a cornerstone of everything you do.I'm not sure I quite visualise this correctly... It sounds like you're talking about the same problem where people complain with respect to 'const' and you happily tell them to stop whingeing and deal with it? (FWIW, I think you made the right call on const. It's never caused me any hair loss.) So you're saying that a pointer itself shouldn't be able to escape a call tree, but the thing it points to should be able to escape just fine? It feels like that kinda defeats the purpose... or at least makes the concept about as reliable as 'const' is in C++. I guess you're seeing a situation where 'scope' is almost exclusively useful as a mechanism to tell the RC that it doesn't need to worry about ref-fiddling, and the thing we're passing isn't interested in scope restrictions at any level other than that RC optimisation? I guess I can see your angle... but my reaction is if that's all you are concerned about, maybe scope is the wrong tool for eliding ref fiddling in that case :/ I can see how you arrived at the opinion from your other comment (wherever it was) that you are kinda likening this whole problem to the RC case, if that's how you're looking at it. It sounds like what you want is something like head-scope, in the same way that people often want head-const?2. I feel it's a big mistake to separate it from the type system, which I think most agree, is D's greatest asset by far. Manipulating types is simple and convenient using the type system, and I think manipulating scope will be just as important as any other attribute as soon as even slightly complex use cases begin to arise.Consider a ref counted type, RC!T. If scope were transitive, then you could not have, say, a tree where the edges were RC!T. I.e., the payload of an RC type should not be forced to be scope.
Dec 12 2014
On 12/12/2014 4:19 AM, Manu via Digitalmars-d wrote:You've said this before, many times, but what is lacking is an explanation of WHY. What is the pattern, and why do you need that pattern? You say you don't like auto ref because it doesn't give you exact control, but what are the cases where auto ref is wrong?I simply do not understand why distinguishing beteen ref and not-ref is a cornerstone of everything you do.I've said so many times, it's the single greatest regular frustration I encounter, by far. That is of course a relative measure. It's not the 'cornerstone of everything I do'; I don't start discussions about things that are not broken and otherwise painless. It is the thing that is *the most broken*, and leads to the most edge cases, general bloat, and text mixins. It comes up the most frequently, and by that metric alone, I consider it highest priority on my list. I've also said many times before, it possibly stems from the fact that one of the key cornerstones of almost everything I do in D is interact with other languages. This is a practical reality, I can't write all my code in D, and have mountains of existing code to interact with. Boilerplate is the result. D has powerful systems to automate boilerplate, like a carrot dangling right in front of my face, but it's almost always thwarted by ref, and almost exclusively so. My recent work updating LuaD to support all the features I required wasted about 80% of my time dealing with ref issues. In my past C++ bindings solutions, much code, which was otherwise simple, readable, and elegant, turned into a mess of text mixins, almost exclusively because ref is broken.struct Tree { RefCount!(Tree*) left; RefCount!(Tree*) right; ... }Consider a ref counted type, RC!T. If scope were transitive, then you could not have, say, a tree where the edges were RC!T. I.e., the payload of an RC type should not be forced to be scope.I'm not sure I quite visualise this correctly...So you're saying that a pointer itself shouldn't be able to escape a call tree, but the thing it points to should be able to escape just fine?Yes.It feels like that kinda defeats the purpose...You're arguing that a data structure with only one access point is the only kind of data structure in use. With transitive scope, such a data structure would be the only one possible!I guess you're seeing a situation where 'scope' is almost exclusively useful as a mechanism to tell the RC that it doesn't need to worry about ref-fiddling, and the thing we're passing isn't interested in scope restrictions at any level other than that RC optimisation? I guess I can see your angle... but my reaction is if that's all you are concerned about, maybe scope is the wrong tool for eliding ref fiddling in that case :/'scope' is a way to say that this use of a pointer does not escape this scope. That is incredibly useful. Recall my statements that pervasive refcounting is a terrible performance problem because of all the inc/dec? Knowing that references cannot escape means an inc/dec pair can be elided.
Dec 12 2014
On 13 December 2014 at 08:14, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 12/12/2014 4:19 AM, Manu via Digitalmars-d wrote:I did just give some examples, I'll repeat; auto ref fails when the function is extern. It is also wrong that when I pass an int or float, it is passed by ref instead of by value... that is never what I want! What do you get when you take a pointer of a function with an auto-ref arg? ...an error, because it's not actually a function! So in a context where I'm dealing with functions and function pointers (very, very frequent), I suddenly have something that's not a function find it's way into my meta and I have to special-case the hell out of it. The solution in this case is to wrap it in a non-auto-ref shim with the ref-ness explicitly stated in the way I expect... which is in itself another problem, because 'ref' is not part of the type! So the annoying de-auto-ref-ing shim must be a text mixin with some very complex, practically unreadable, definitely unmaintainable logic surrounding it. It's insanity on top of insanity! I also run the invisible risk of generating 2^num_args instances of the code for functions containing auto-ref args. When I'm writing a function that's not a template, I intend, and expect, to write a function that's _not a template_. Templates and functions are different things. I think it's a massive mistake to have created a way to write a template that looks nothing like a template. auto-ref is not, and never has been a tool I have wanted. I don't have any use for auto-ref, and it has only proven to make an already severe problem worse. I've tried to use it before in some instances, but it made ref's out of int's and floats, so even in the rare potentially useful cases, I had to reject it. At the time, you introduced auto-ref as a 'solution' to an issue that I was the primary complainant (although the solution was mainly pushed by Andrei iirc?). I absolutely rejected it then, and said it would have the disastrous effect of setting ref in stone, which I was already very critical about. I reject it even more now that I have had some experience with it. Who was the customer that you designed it for? (It wasn't me!) What issue was it designed to address?You've said this before, many times, but what is lacking is an explanation of WHY. What is the pattern, and why do you need that pattern? You say you don't like auto ref because it doesn't give you exact control, but what are the cases where auto ref is wrong?I simply do not understand why distinguishing beteen ref and not-ref is a cornerstone of everything you do.I've said so many times, it's the single greatest regular frustration I encounter, by far. That is of course a relative measure. It's not the 'cornerstone of everything I do'; I don't start discussions about things that are not broken and otherwise painless. It is the thing that is *the most broken*, and leads to the most edge cases, general bloat, and text mixins. It comes up the most frequently, and by that metric alone, I consider it highest priority on my list. I've also said many times before, it possibly stems from the fact that one of the key cornerstones of almost everything I do in D is interact with other languages. This is a practical reality, I can't write all my code in D, and have mountains of existing code to interact with. Boilerplate is the result. D has powerful systems to automate boilerplate, like a carrot dangling right in front of my face, but it's almost always thwarted by ref, and almost exclusively so. My recent work updating LuaD to support all the features I required wasted about 80% of my time dealing with ref issues. In my past C++ bindings solutions, much code, which was otherwise simple, readable, and elegant, turned into a mess of text mixins, almost exclusively because ref is broken.... I don't think I'd ever have a use for this code. I've been using trees for a long time, and I can't imagine a situation where each node in a tree would want to be ref counted. It sounds like a disaster for performance, and I can't imagine any advantage it would offer? Perhaps in some complex graph structure... but I expect that sort of thing would be far more specialised. I can see common cases where nodes may contain a refcounted object as node data, but that's different than the situation you demonstrate here. I'll need to think about how that situation would be affected in this case.struct Tree { RefCount!(Tree*) left; RefCount!(Tree*) right; ... }Consider a ref counted type, RC!T. If scope were transitive, then you could not have, say, a tree where the edges were RC!T. I.e., the payload of an RC type should not be forced to be scope.I'm not sure I quite visualise this correctly...Surely the possibility remains for such a data structure that is NOT scope... but I think I can see your point. There is a common case, ie, refcounting, where we want to be able to elide rc fiddling, and that may be the sole call for scope in those cases. It seems like that situation calls for something like head-scope. I think transitive scope has a different application, useful in different situations. It seems a problem that a struct that contains a single pointer, which is passed by value, can't have scope applied under your proposal(?). I use this pattern in almost every interaction with other languages that I encounter. Some external API which operates via opaque pointer, wants to be wrapped in such a way that it maintains it's behaviour as a reference type, but also gains convenient member-style access. It's kinda making a pseudo-class out of something that's not a class, and scope needs to support this.So you're saying that a pointer itself shouldn't be able to escape a call tree, but the thing it points to should be able to escape just fine?Yes.It feels like that kinda defeats the purpose...You're arguing that a data structure with only one access point is the only kind of data structure in use. With transitive scope, such a data structure would be the only one possible!I know this. I'm not arguing against any proposition that we need scope to make RC practical. I've been arguing in favour of that for at least as long as I've been batting for the ARC team, and even then some more, because it would also address rvalue->ref, which is another massive pain point! But there are more things than pointers which need to be protected against escaping their scope, in particular, things that contain pointers... Maybe there's a compromise. If we say scope isn't 'transitive', but it is transitive when it comes to aggregates? Ie, you can apply scope to a by-val struct, and all aggregate members have scope applied. It is not transitive in the sense that it does not follow through pointer members, but any such pointer members may not escape as if it were a pointer argument alone. This is unlike any other behaviour in D, but without that, I think this proposal fails to suit my most common use cases.I guess you're seeing a situation where 'scope' is almost exclusively useful as a mechanism to tell the RC that it doesn't need to worry about ref-fiddling, and the thing we're passing isn't interested in scope restrictions at any level other than that RC optimisation? I guess I can see your angle... but my reaction is if that's all you are concerned about, maybe scope is the wrong tool for eliding ref fiddling in that case :/'scope' is a way to say that this use of a pointer does not escape this scope. That is incredibly useful. Recall my statements that pervasive refcounting is a terrible performance problem because of all the inc/dec? Knowing that references cannot escape means an inc/dec pair can be elided.
Dec 12 2014
On 12/12/2014 6:55 PM, Manu via Digitalmars-d wrote:I did just give some examples, I'll repeat; auto ref fails when the function is extern.Don't make it extern, then.It is also wrong that when I pass an int or float, it is passed by ref instead of by value... that is never what I want!If there's source to the function, it'll often be inlined which will remove the indirection.What do you get when you take a pointer of a function with an auto-ref arg? ...an error, because it's not actually a function! So in a context where I'm dealing with functions and function pointers (very, very frequent), I suddenly have something that's not a function find it's way into my meta and I have to special-case the hell out of it.Why are function pointers and ints going to the same argument of a function? I thought you weren't using templates?The solution in this case is to wrap it in a non-auto-ref shim with the ref-ness explicitly stated in the way I expect... which is in itself another problem, because 'ref' is not part of the type! So the annoying de-auto-ref-ing shim must be a text mixin with some very complex, practically unreadable, definitely unmaintainable logic surrounding it. It's insanity on top of insanity! I also run the invisible risk of generating 2^num_args instances of the code for functions containing auto-ref args.I wonder what is the need for the code that you are writing.When I'm writing a function that's not a template, I intend, and expect, to write a function that's _not a template_. Templates and functions are different things. I think it's a massive mistake to have created a way to write a template that looks nothing like a template.A function template is a function that takes both compile-time args and run-time args. C++ tried to make them completely different animals, when they are not. Don't think "template", think "compile-time argument to a function". I convinced Andrei to never mention "template" in his D book, as it brings forth all kinds of connotations and expectations that impede understanding what D templates actually are. I think the result was successful.auto-ref is not, and never has been a tool I have wanted. I don't have any use for auto-ref, and it has only proven to make an already severe problem worse. I've tried to use it before in some instances, but it made ref's out of int's and floats, so even in the rare potentially useful cases, I had to reject it.If it's a rare useful case, why is it a pervasive problem?At the time, you introduced auto-ref as a 'solution' to an issue that I was the primary complainant (although the solution was mainly pushed by Andrei iirc?). I absolutely rejected it then, and said it would have the disastrous effect of setting ref in stone, which I was already very critical about. I reject it even more now that I have had some experience with it. Who was the customer that you designed it for? (It wasn't me!) What issue was it designed to address?I still have no idea what code you are developing that you need to send ints and function pointers to the same argument of a function template, yet you don't use function templates. Nor do I understand what pattern you need that simply must mix up ref and value parameters, and why that pattern appears pervasively in your code.You have no use for tree structures?struct Tree { RefCount!(Tree*) left; RefCount!(Tree*) right; ... }... I don't think I'd ever have a use for this code.I've been using trees for a long time, and I can't imagine a situation where each node in a tree would want to be ref counted.You have more than one parent of a node. You never write data structures like that? dmd uses such pervasively (Type and Expression).It sounds like a disaster for performance, and I can't imagine any advantage it would offer?How do you propose to manage the memory explicitly otherwise? Use a GC? :-)I can see common cases where nodes may contain a refcounted object as node data, but that's different than the situation you demonstrate here.That would fail if scope were transitive.But there are more things than pointers which need to be protected against escaping their scope, in particular, things that contain pointers...Solve it in the general case (this proposal) and RC now works.Maybe there's a compromise. If we say scope isn't 'transitive', but it is transitive when it comes to aggregates?One thing I've tried very hard to make work in D is have basic types / aggregages / arrays be interchangeable.Ie, you can apply scope to a by-val struct, and all aggregate members have scope applied. It is not transitive in the sense that it does not follow through pointer members, but any such pointer members may not escape as if it were a pointer argument alone. This is unlike any other behaviour in D, but without that, I think this proposal fails to suit my most common use cases.I have no idea what your use cases are, but when you're explicitly managing memory, every damn pointer needs to be carefully evaluated as to what exactly it's pointing two and who owns it. Scope is not a magic solution to this, neither is shared_ptr, neither are Rust's annotations. The only scheme that absolves the user of having to deal with this is - GC.
Dec 12 2014
Walter Bright:Giving a reference counter to every pointer in a binary tree sounds a bit too much. Bye, bearophileYou have no use for tree structures?struct Tree { RefCount!(Tree*) left; RefCount!(Tree*) right; ... }... I don't think I'd ever have a use for this code.
Dec 13 2014
On 12/13/2014 2:10 AM, bearophile wrote:Walter Bright:DMD, for example, uses ASTs where the nodes have multiple parents.Giving a reference counter to every pointer in a binary tree sounds a bit too much.You have no use for tree structures?struct Tree { RefCount!(Tree*) left; RefCount!(Tree*) right; ... }... I don't think I'd ever have a use for this code.
Dec 13 2014
On Saturday, 13 December 2014 at 20:45:53 UTC, Walter Bright wrote:On 12/13/2014 2:10 AM, bearophile wrote:It is a DAG then.Walter Bright:DMD, for example, uses ASTs where the nodes have multiple parents.Giving a reference counter to every pointer in a binary tree sounds a bit too much.You have no use for tree structures?struct Tree { RefCount!(Tree*) left; RefCount!(Tree*) right; ... }... I don't think I'd ever have a use for this code.
Dec 15 2014
On 2014-12-13 06:11, Walter Bright wrote:Don't make it extern, then.He's said a couple of time he's interfacing with C and/or C++ code.Why are function pointers and ints going to the same argument of a function? I thought you weren't using templates?I think he meant taking the address of a function with auto ref. -- /Jacob Carlborg
Dec 13 2014
On 12/13/2014 8:19 AM, Jacob Carlborg wrote:On 2014-12-13 06:11, Walter Bright wrote:Yes, and C++ code is either ref or not ref, and it is not a problem to insert 'ref' or not, manually.Don't make it extern, then.He's said a couple of time he's interfacing with C and/or C++ code.Right, but why is that even being attempted?Why are function pointers and ints going to the same argument of a function? I thought you weren't using templates?I think he meant taking the address of a function with auto ref.
Dec 13 2014
On 13 December 2014 at 15:11, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 12/12/2014 6:55 PM, Manu via Digitalmars-d wrote:Do you think I just interact with other languages for fun or something?I did just give some examples, I'll repeat; auto ref fails when the function is extern.Don't make it extern, then.Not if it's extern (applicable to ref, not auto-ref), or wrapped, or if I ever want to capture a function pointer. It's also semantically different; I can change the caller's value.It is also wrong that when I pass an int or float, it is passed by ref instead of by value... that is never what I want!If there's source to the function, it'll often be inlined which will remove the indirection.I'm confused. I'm talking about capturing function pointers. Not about function arguments. Capturing pointers of functions with auto-ref args (aka, template functions) is a serious nuisance, and impossible outside the site of instantiation. You need information that's only available at the point of instantiation. That changes the landscape quite a bit.What do you get when you take a pointer of a function with an auto-ref arg? ...an error, because it's not actually a function! So in a context where I'm dealing with functions and function pointers (very, very frequent), I suddenly have something that's not a function find it's way into my meta and I have to special-case the hell out of it.Why are function pointers and ints going to the same argument of a function? I thought you weren't using templates?General function adaptation. There are lots of reasons to wrap functions. Adaptation to existing or external API's is the most common in my experience. Functions are what programs are! I really struggle to understand why you have such trouble sympathising with my view here. Languages generate code, which is packaged into blocks we call 'functions'... and they require to adhere to strict ABI requirements. That is programming in a nutshell. Surely it's reasonable to want to retain complete control over this most primitive and basic of tasks. One important aspect of that control is where the code is generated; is it 'here', or 'there'? And that's a critical distinction between functions and templates. Look at LuaD. Lots of awkward cases have emerged there. There are many more to come too; the known bug list are mostly nasty issues of this nature that I've been putting off. I can't offer any insight into my commercial code sadly, and I no longer have access to it :/ Situations like this appear frequently: https://github.com/JakobOvrum/LuaD/pull/76/files#diff-bcb370a5bc6fe75a9d5c04f2e1c17eb0R178 And something like this tends to appear as one aspect of the solution: https://github.com/JakobOvrum/LuaD/pull/76/files#diff-bcb370a5bc6fe75a9d5c04f2e1c17eb0R68 'struct Ref(T)' leads to its own problems though, in that it's a localised concept. No external code anywhere understands it as a 'ref', so if 3rd party code has any special treatment for ref, that code now fails. Then more magic like this: https://github.com/JakobOvrum/LuaD/pull/76/files#diff-ec8c532aeca798240de4d70ee639fc16R90 Since we need to recognise 'ref' and substitute it for our magic 'struct Ref(T)'. I've probably spent more hours wrangling D meta of this sort than most people. This sort of thing always happens. If Ref!T is the tool that resolves the situation, then it's clear demonstration that ref should be part of the type.The solution in this case is to wrap it in a non-auto-ref shim with the ref-ness explicitly stated in the way I expect... which is in itself another problem, because 'ref' is not part of the type! So the annoying de-auto-ref-ing shim must be a text mixin with some very complex, practically unreadable, definitely unmaintainable logic surrounding it. It's insanity on top of insanity! I also run the invisible risk of generating 2^num_args instances of the code for functions containing auto-ref args.I wonder what is the need for the code that you are writing.You can spin it however you like, but it's exactly the same thing. They are completely different animals. A function template is not a function at all until it's instantiated. When it's instantiated, it becomes a function, or even, one of a suite of functions. And it's not known to the author where the function is. (Note: I define 'function' in this context to mean 'some code that is emitted') This one:many relationship between definition and code creates some sorts of problems that aren't present with functions, which relate 1:1 with their codegen. It is possible to take a pointer to a function. It is not possible to take a pointer to a template, unless your code exists at the callsite where the parameters for instantiation are known. This makes certain things more complicated. I reject that 'a template is a function with compile time args'. Template functions have rather different characteristics, which result in special consideration, and some restrictions on usage. It's a cute idea, and something that might sound nice in a book... but it's not the reality. 'functions' are a much simpler, more fundamental concept, and also one that is easily portable between languages.When I'm writing a function that's not a template, I intend, and expect, to write a function that's _not a template_. Templates and functions are different things. I think it's a massive mistake to have created a way to write a template that looks nothing like a template.A function template is a function that takes both compile-time args and run-time args. C++ tried to make them completely different animals, when they are not. Don't think "template", think "compile-time argument to a function". I convinced Andrei to never mention "template" in his D book, as it brings forth all kinds of connotations and expectations that impede understanding what D templates actually are. I think the result was successful.I never argued that auto-ref wasn't rare, but it was *a mistake*. What it did was cement an already very shaky language feature into the foundation. Once there is layers of language built on top of something, it becomes that much harder to refactor at some later time. I'm not saying auto-ref is a pervasive problem, I'm saying (over and over again) that *ref* is a pervasive problem, and auto-ref turns out to be a further (fairly rare) nuisance. It can't be presented as a solution to any problem that I have (how we got onto this tangent), because it's not. I also don't have any idea why it exists! What's it for? You love to make people justify exactly what things are for; you're doing it to me in almost every paragraph. In my case here though, I'm not asking you to invent a language feature like auto-ref, I'm just commenting on the usefulness of an existing feature. Trouble is, my cases are really hard to define and highly context specific. I'm sharing my anecdotal experience that D's ref is an awkward pain in the arse at best, and by my judgement, a failed experiment.auto-ref is not, and never has been a tool I have wanted. I don't have any use for auto-ref, and it has only proven to make an already severe problem worse. I've tried to use it before in some instances, but it made ref's out of int's and floats, so even in the rare potentially useful cases, I had to reject it.If it's a rare useful case, why is it a pervasive problem?I was talking about taking function pointers of functions. Not passing function pointers to functions. The auto-ref detail was on a slight tangent; explaining that it's not useful to me to solve the problem of dealing with ref-ness of incoming functions args or results. I think I said that clearly: "What do you get when you take a pointer of a function with an auto-ref arg? ...an error, because it's not actually a function!" WRT to mixing ref and value parameters; I'm wrapping/adapting existing api's. I have no control over the code/api that exists. If that api uses ref, for whatever reason that it makes sense to do so, I need to handle the case. There's no 'pattern', only a mechanical process.At the time, you introduced auto-ref as a 'solution' to an issue that I was the primary complainant (although the solution was mainly pushed by Andrei iirc?). I absolutely rejected it then, and said it would have the disastrous effect of setting ref in stone, which I was already very critical about. I reject it even more now that I have had some experience with it. Who was the customer that you designed it for? (It wasn't me!) What issue was it designed to address?I still have no idea what code you are developing that you need to send ints and function pointers to the same argument of a function template, yet you don't use function templates. Nor do I understand what pattern you need that simply must mix up ref and value parameters, and why that pattern appears pervasively in your code.I don't have use for a tree where every node is ref counted... tree nodes should only have one reference; their parent.You have no use for tree structures?struct Tree { RefCount!(Tree*) left; RefCount!(Tree*) right; ... }... I don't think I'd ever have a use for this code.That's not a tree, that's a graph. I made a comment on such graph structures: "Perhaps in some complex graph structure... but I expect that sort of thing would be far more specialised." And I still think that. I'm not going to rule out that an RC graph node might be useful, but I was responding to your example, and that wasn't your example.I've been using trees for a long time, and I can't imagine a situation where each node in a tree would want to be ref counted.You have more than one parent of a node. You never write data structures like that? dmd uses such pervasively (Type and Expression).Tree elements only have one reference; their parent. Management of trees is very simple. Graph's may be a little more complex, and I can see that an RC node might be useful... but I've been using various graph structures for a long time, and never wanted to RC the nodes. I expect traditional patterns would remain. I don't think an RC is required to know if a graph node is still referenced within the graph; graph nodes typically have bi-directional linkage, so when there are no links to the node, then it is unreferenced, and can easily be destroyed/returned to a pool. I think an RC mechanism is effectively implicit in graph structures I can imagine. The cases where I use RC are for general 'resources'. In my line of work, this is textures, mesh, renderstate, sounds, etc... things that find themselves being shared around arbitrarily. It is important that tree/graph nodes may contain a ref-counted thing, but I have trouble visualising how that affects 'scope' in this case.It sounds like a disaster for performance, and I can't imagine any advantage it would offer?How do you propose to manage the memory explicitly otherwise? Use a GC? :-)I'm not sure I see why... The situation is: a whole transitive scope graph is received as scope, how does that inhibit my accessing a resource contained in the graph? It inhibits my _escaping_ said resource... as it should. Right? The problem you're addressing is that, if the graph isn't received as scope (ie, I want the ability to escape it's internals), then RC doesn't work efficiently. My reaction is that RC optimisation is perhaps not strongly associated with 'scope'. scope may offer a mechanism by which it can work some of the time, but it's not precisely the same thing. I kinda feel like you're trying to make scope into RC optimisation, rather than RC optimisation into scope...? I was once arguing for: int^ rcInt; I think I'm going back in that direction. That's what other languages do. And scope would create additional opportunity here; it would allow implicit casting of T^ -> T*.I can see common cases where nodes may contain a refcounted object as node data, but that's different than the situation you demonstrate here.That would fail if scope were transitive.Perhaps scope and RC are different things? Solve scope purely for RC, and you've broken scope for other purposes that it's useful; that is, inhibiting escaping of data.But there are more things than pointers which need to be protected against escaping their scope, in particular, things that contain pointers...Solve it in the general case (this proposal) and RC now works.I'm looking for ways to make scope fit your proposed mould and be useful to me. If I can't do this then my most common use case is unsatisfied: struct Wrap { OpaqueThing *ptr; this() {} ~this() {} // <- constructors that twiddle RC effectively. this(this) {} this() scope {} ~this() scope {} // <- Overloadable for 'scope', in which case, don't twiddle the RC! this(this) scope {} // lots of operations on OpaqueThing wrapped up here... } void f(scope Wrap x) <-- RC twiddling is effectively elided here due to constructor overloads { } If you can propose an alternative solution? This is representative of my 99% case. Sometimes the struct is more than a single pointer though.Maybe there's a compromise. If we say scope isn't 'transitive', but it is transitive when it comes to aggregates?One thing I've tried very hard to make work in D is have basic types / aggregages / arrays be interchangeable.scope just needs to say "I will not let this memory escape". Then it finally allows us to safely put data on the stack, something we can't do in D today. I don't think scope should aim to do anything more than that. If scope can be useful to RC, that's great! But from this, it's starting to look to me like scope might be partially useful to RC, but scope and RC aren't exactly the same thing. RC seems to require something like head-scope in order to not place awkward restrictions on usage of RC objects. That said, full-scope isn't without use cases; it allows us to safely use the stack, potentially eliminating much GC load.Ie, you can apply scope to a by-val struct, and all aggregate members have scope applied. It is not transitive in the sense that it does not follow through pointer members, but any such pointer members may not escape as if it were a pointer argument alone. This is unlike any other behaviour in D, but without that, I think this proposal fails to suit my most common use cases.I have no idea what your use cases are, but when you're explicitly managing memory, every damn pointer needs to be carefully evaluated as to what exactly it's pointing two and who owns it. Scope is not a magic solution to this, neither is shared_ptr, neither are Rust's annotations. The only scheme that absolves the user of having to deal with this is - GC.
Dec 13 2014
On Sunday, 14 December 2014 at 00:45:06 UTC, Manu via Digitalmars-d wrote:On 13 December 2014 at 15:11, Walter Bright via Digitalmars-dPhilippe Sigaud Excellent "D Templates: A Tutorial"... the MANTRA... "XXX templates are not XXXs, they are templates. With XXX being any of (function, struct, class, interface, union)." --- /PaoloA function template is a function that takes both compile-time args and run-time args. C++ tried to make them completely different animals, when they are not. Don't think "template", think "compile-time argument to a function". I convinced Andrei to never mention "template" in his D book, as it brings forth all kinds of connotations and expectations that impede understanding what D templates actually are. I think the result was successful.You can spin it however you like, but it's exactly the same thing. They are completely different animals. A function template is not a function at all until it's instantiated. When it's instantiated, it becomes a function, or even, one of a suite of functions. And it's not known to the author where the function is. (Note: I define 'function' in this context to mean 'some code that is emitted')
Dec 14 2014
On Sun, 14 Dec 2014 09:04:58 +0000 Paolo Invernizzi via Digitalmars-d <digitalmars-d puremagic.com> wrote:Philippe Sigaud Excellent "D Templates: A Tutorial"... the MANTRA... =20 "XXX templates are not XXXs, they are templates. With XXX being=20 any of (function, struct, class, interface, union)."and that still doesn't work. ;-) as a somehow related example i recall my first expirience with C. my first "serious" language was Pascal, and it has that nice range-checking feature for arrays. it was very hard for me to understand that C has no arrays at all, not to say range checking. why there is no arrays if i have this nice indexing and all that?! what do you mean by that "buffer overflow" nonsence? if it looks like an array it must be an array!
Dec 14 2014
On Sunday, 14 December 2014 at 00:45:06 UTC, Manu via Digitalmars-d wrote:Perhaps scope and RC are different things? Solve scope purely for RC, and you've broken scope for other purposes that it's useful; that is, inhibiting escaping of data.Exactly. It shouldn't matter which type this data has - both references and value types are valid. I would define `scope` like this: "A value designated with `scope` may not be assigned to a variable (or parameter, ...) with a lifetime longer than its owner's lifetime."It should only be applicable to whatever is explicitly marked as `scope`. For references, that's the reference itself, but not what it points to. For aggregates, it's the entire aggregate with all it's members. That way, all types will be treated uniformly. Of course, this can only work with a type modifier, not with a storage class.Maybe there's a compromise. If we say scope isn't 'transitive', but it is transitive when it comes to aggregates?One thing I've tried very hard to make work in D is have basic types / aggregages / arrays be interchangeable.I'm looking for ways to make scope fit your proposed mould and be useful to me. If I can't do this then my most common use case is unsatisfied: struct Wrap { OpaqueThing *ptr; this() {} ~this() {} // <- constructors that twiddle RC effectively. this(this) {} this() scope {} ~this() scope {} // <- Overloadable for 'scope', in which case, don't twiddle the RC! this(this) scope {} // lots of operations on OpaqueThing wrapped up here... } void f(scope Wrap x) <-- RC twiddling is effectively elided here due to constructor overloads { } If you can propose an alternative solution? This is representative of my 99% case. Sometimes the struct is more than a single pointer though.Unfortunately Walter rejected this when I had proposed it. In the current proposal, we cannot overload on scope. The proposed solution is to not "borrow" the wrapper, but only its payload. But this is restrictive; even in the case of RC, there are use-cases that cannot be served with this technique, namely when the callee can only decide at runtime whether it wants to make a copy of an RC value it received. The correct solution would be to pass the RC wrapper itself as scope, as you show in your example, thereby delegating the decision of whether to adjust the refcount from the caller to the callee.
Dec 14 2014
On 12/13/2014 4:44 PM, Manu via Digitalmars-d wrote:On 13 December 2014 at 15:11, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:I'm not trying to insult you - but why need 'auto ref' for extern functions? The extern functions are ref or not-ref, and declare the interface to match. There's no point to auto ref there.On 12/12/2014 6:55 PM, Manu via Digitalmars-d wrote:Do you think I just interact with other languages for fun or something?I did just give some examples, I'll repeat; auto ref fails when the function is extern.Don't make it extern, then.Again, if it is extern it has a fixed definition of ref or not. The D side doesn't decide if it's ref or not, it just follows whatever the extern declaration is. This is why I do not understand why you are running into this issue everywhere.If there's source to the function, it'll often be inlined which will remove the indirection.Not if it's extern (applicable to ref, not auto-ref), or wrapped, or if I ever want to capture a function pointer. It's also semantically different; I can change the caller's value.WHY are you doing this? I have no idea what coding pattern would need to pass functions by reference.I'm confused. I'm talking about capturing function pointers. Not about function arguments. Capturing pointers of functions with auto-ref args (aka, template functions) is a serious nuisance, and impossible outside the site of instantiation.What do you get when you take a pointer of a function with an auto-ref arg? ...an error, because it's not actually a function! So in a context where I'm dealing with functions and function pointers (very, very frequent), I suddenly have something that's not a function find it's way into my meta and I have to special-case the hell out of it.Why are function pointers and ints going to the same argument of a function? I thought you weren't using templates?Are those API's all templated? My guess is no. If they're not, you don't need templates or auto-ref to interface to them.I wonder what is the need for the code that you are writing.General function adaptation. There are lots of reasons to wrap functions. Adaptation to existing or external API's is the most common in my experience.Functions are what programs are! I really struggle to understand why you have such trouble sympathising with my view here.Because you don't provide any examples of why you need this stuff. You say "I need this feature because I'm doing X", but I have no idea what X is or why you need X. I.e. you present and advocate for particular solutions, but not the problems.Languages generate code, which is packaged into blocks we call 'functions'... and they require to adhere to strict ABI requirements. That is programming in a nutshell. Surely it's reasonable to want to retain complete control over this most primitive and basic of tasks. One important aspect of that control is where the code is generated; is it 'here', or 'there'?Why is that important?And that's a critical distinction between functions and templates.You can control that with function templates, too. The compiler won't instantiate a template locally if the import instantiates it.Look at LuaD. Lots of awkward cases have emerged there. There are many more to come too; the known bug list are mostly nasty issues of this nature that I've been putting off. I can't offer any insight into my commercial code sadly, and I no longer have access to it :/ Situations like this appear frequently: https://github.com/JakobOvrum/LuaD/pull/76/files#diff-bcb370a5bc6fe75a9d5c04f2e1c17eb0R178 And something like this tends to appear as one aspect of the solution: https://github.com/JakobOvrum/LuaD/pull/76/files#diff-bcb370a5bc6fe75a9d5c04f2e1c17eb0R68 'struct Ref(T)' leads to its own problems though, in that it's a localised concept. No external code anywhere understands it as a 'ref', so if 3rd party code has any special treatment for ref, that code now fails. Then more magic like this: https://github.com/JakobOvrum/LuaD/pull/76/files#diff-ec8c532aeca798240de4d70ee639fc16R90 Since we need to recognise 'ref' and substitute it for our magic 'struct Ref(T)'. I've probably spent more hours wrangling D meta of this sort than most people. This sort of thing always happens. If Ref!T is the tool that resolves the situation, then it's clear demonstration that ref should be part of the type.I'll take a look at your references in a moment.That's the C++ view of templates, which is outdated and unnecessary.You can spin it however you like, but it's exactly the same thing. They are completely different animals. A function template is not a function at all until it's instantiated. When it's instantiated, it becomes a function, or even, one of a suite of functions. And it's not known to the author where the function is. (Note: I define 'function' in this context to mean 'some code that is emitted')When I'm writing a function that's not a template, I intend, and expect, to write a function that's _not a template_. Templates and functions are different things. I think it's a massive mistake to have created a way to write a template that looks nothing like a template.A function template is a function that takes both compile-time args and run-time args. C++ tried to make them completely different animals, when they are not. Don't think "template", think "compile-time argument to a function". I convinced Andrei to never mention "template" in his D book, as it brings forth all kinds of connotations and expectations that impede understanding what D templates actually are. I think the result was successful.This one:many relationship between definition and code creates some sorts of problems that aren't present with functions, which relate 1:1 with their codegen. It is possible to take a pointer to a function. It is not possible to take a pointer to a template, unless your code exists at the callsite where the parameters for instantiation are known.It's not possible to take a pointer to a function unless that function exists, either.This makes certain things more complicated.I don't see a problem with taking the address of an instantiated template function.I reject that 'a template is a function with compile time args'. Template functions have rather different characteristics, which result in special consideration, and some restrictions on usage. It's a cute idea, and something that might sound nice in a book... but it's not the reality.I've found templates to be far more useful when viewed that way.'functions' are a much simpler, more fundamental concept, and also one that is easily portable between languages.It's always going to be true that in order to interface D with X, you'll have to use a D subset that X understands. BTW, D can interface to C++ template functions!I also don't have any idea why it exists! What's it for?So you can make a template work with both lvalues and rvalues - and implement perfect forwarding.You love to make people justify exactly what things are for;It's not a question of loving it, it's a question of practicality.I think I said that clearly: "What do you get when you take a pointer of a function with an auto-ref arg? ...an error, because it's not actually a function!"What's baffling to me is why even declare a function pointer parameter as auto-ref? I don't use floating point to index strings, either, that doesn't mean floating point is a failed concept.WRT to mixing ref and value parameters; I'm wrapping/adapting existing api's. I have no control over the code/api that exists. If that api uses ref, for whatever reason that it makes sense to do so, I need to handle the case. There's no 'pattern', only a mechanical process.If the existing code uses ref, use ref on the D side. If it does not, do not use ref on the D side. What's the problem?That's not a tree, that's a graph.Sure. Do you want a scope design that precludes graphs? And even with trees, are you sure you want a design where nobody but the root can point to anything in the tree? How would you do a symbol table? You couldn't ever return a pointer to the found symbol if it was all scoped.Tree elements only have one reference; their parent. Management of trees is very simple.Again, what about the contents of the tree?How would you manage a symbol table lookup, and then store a pointer to the found symbol in the AST?That would fail if scope were transitive.I'm not sure I see why... The situation is: a whole transitive scope graph is received as scope, how does that inhibit my accessing a resource contained in the graph? It inhibits my _escaping_ said resource... as it should. Right?I was once arguing for: int^ rcInt; I think I'm going back in that direction. That's what other languages do. And scope would create additional opportunity here; it would allow implicit casting of T^ -> T*.scope actually does that. (int^ rcInt; isn't transitive, either.)Perhaps scope and RC are different things? Solve scope purely for RC, and you've broken scope for other purposes that it's useful; that is, inhibiting escaping of data.RC is the canonical use of scope. The beauty is the compiler does not have to recognize RC as special. It becomes just ordinary library code.struct Wrap { OpaqueThing *ptr; this() {} ~this() {} // <- constructors that twiddle RC effectively. this(this) {} this() scope {} ~this() scope {} // <- Overloadable for 'scope', in which case, don't twiddle the RC! this(this) scope {} // lots of operations on OpaqueThing wrapped up here... } void f(scope Wrap x) <-- RC twiddling is effectively elided here due to constructor overloads { }First off, get rid of the scope this overloads. Second, add an 'alias this' to implement a default conversion of Wrap to OpaqueThing*. Third, declare f as: void f(scope ref OpaqueThing* x);scope just needs to say "I will not let this memory escape". Then it finally allows us to safely put data on the stack, something we can't do in D today. I don't think scope should aim to do anything more than that.That's EXACTLY what this proposal is!If scope can be useful to RC, that's great! But from this, it's starting to look to me like scope might be partially useful to RC, but scope and RC aren't exactly the same thing.Wheels are cars aren't the same thing, either, but cars need wheels.RC seems to require something like head-scope in order to not place awkward restrictions on usage of RC objects. That said, full-scope isn't without use cases; it allows us to safely use the stack, potentially eliminating much GC load.This proposal IS head-scope.
Dec 14 2014
On Sunday, 14 December 2014 at 23:27:15 UTC, Walter Bright wrote:That's not what he's talking about. My understanding is that Manu is talking about something like this: void foo(auto ref someConcreteType) { ... } auto fooP = &foo; //error, foo is not a functionI think I said that clearly: "What do you get when you take a pointer of a function with an auto-ref arg? ...an error, because it's not actually a function!"What's baffling to me is why even declare a function pointer parameter as auto-ref? I don't use floating point to index strings, either, that doesn't mean floating point is a failed concept.
Dec 15 2014
On Saturday, 13 December 2014 at 02:55:50 UTC, Manu via Digitalmars-d wrote:Templates and functions are different things. I think it's a massive mistake to have created a way to write a template that looks nothing like a template.That is a misconception spread by C++. Templates are pure functions applied at compile time. Function templates are functions partially applied at compile time.
Dec 13 2014
On 12/13/2014 6:10 AM, Max Samukha wrote:On Saturday, 13 December 2014 at 02:55:50 UTC, Manu via Digitalmars-d wrote:It took me an embarrassingly long time to get past the C++ notion that templates are something orthogonal from 'normal' functions and data declarations. Even the C++ notion of a 'primary' template is wholly unnecessary, but is deeply embedded in how it handles the semantics of templates. C++ did get right the partial ordering of templates, and D applied partial ordering to 'normal' functions as well.Templates and functions are different things. I think it's a massive mistake to have created a way to write a template that looks nothing like a template.That is a misconception spread by C++. Templates are pure functions applied at compile time. Function templates are functions partially applied at compile time.
Dec 13 2014
On 2014-12-04 09:24:13 +0000, Walter Bright said:http://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape. This, among other things, makes a ref counting type practical. It also makes it more practical to use other storage allocation schemes than garbage collection. It does not make scope into a type constructor, nor a general type-annotation system. It does not provide an ownership system, though it would complement one.I like the basics of the proposal and I think it's the right direction. HOWEVER, I strongly believe that function arguments should be scoped by default and `impure` when they take reference which they will keep. -Shammah
Dec 10 2014
On 2014-12-11 07:03:58 +0000, Shammah Chancellor said:On 2014-12-04 09:24:13 +0000, Walter Bright said:Also, more feedback. I would suggest that scope variable are the default inside of functions as well as parameters. Using escape analysis, instead of them being errors, make them warnings and implicitly make them impure. -Shammahhttp://wiki.dlang.org/DIP69 Despite its length, this is a fairly simple proposal. It adds the missing semantics for the 'scope' storage class in order to make it possible to pass a reference to a function without it being possible for it to escape. This, among other things, makes a ref counting type practical. It also makes it more practical to use other storage allocation schemes than garbage collection. It does not make scope into a type constructor, nor a general type-annotation system. It does not provide an ownership system, though it would complement one.I like the basics of the proposal and I think it's the right direction. HOWEVER, I strongly believe that function arguments should be scoped by default and `impure` when they take reference which they will keep. -Shammah
Dec 10 2014