From fw at deneb.enyo.de Tue May 1 00:53:47 2012 From: fw at deneb.enyo.de (Florian Weimer) Date: Tue, 01 May 2012 09:53:47 +0200 Subject: [rust-dev] Interesting paper on RC vs GC In-Reply-To: (Sebastian Sylvan's message of "Mon, 30 Apr 2012 23:23:42 -0700") References: Message-ID: <87fwbk8hh0.fsf@mid.deneb.enyo.de> * Sebastian Sylvan: > R. Shahriyar, S. M. Blackburn, and D. Frampton, "Down for the Count? > Getting Reference Counting Back in the Ring," in Proceedings of the > Eleventh ACM SIGPLAN International Symposium on Memory Management, > ISMM ?12, Beijing, China, June 15-16, 2012. > > http://users.cecs.anu.edu.au/~steveb/downloads/pdf/rc-ismm-2012.pdf I think they give up deterministic finalization, which would make this approach not suitable to Rust. From a.stavonin at gmail.com Tue May 1 01:03:20 2012 From: a.stavonin at gmail.com (Alexander Stavonin) Date: Tue, 1 May 2012 17:03:20 +0900 Subject: [rust-dev] Testing facility and shared data Message-ID: <9881C0EA-AFBF-424F-9941-98FEB1233C81@gmail.com> I'm looking for a way for sharing data between all tests in a crate. Shared data or singletons are banned by Rust ideology, isn't it? Also we can not have single initialization point for all test. What is a best way to provide some common data for all tests in a crate or module? From matthieu.monrocq at gmail.com Tue May 1 04:07:28 2012 From: matthieu.monrocq at gmail.com (Matthieu Monrocq) Date: Tue, 1 May 2012 13:07:28 +0200 Subject: [rust-dev] Interesting paper on RC vs GC In-Reply-To: <87fwbk8hh0.fsf@mid.deneb.enyo.de> References: <87fwbk8hh0.fsf@mid.deneb.enyo.de> Message-ID: On Tue, May 1, 2012 at 9:53 AM, Florian Weimer wrote: > * Sebastian Sylvan: > > > R. Shahriyar, S. M. Blackburn, and D. Frampton, "Down for the Count? > > Getting Reference Counting Back in the Ring," in Proceedings of the > > Eleventh ACM SIGPLAN International Symposium on Memory Management, > > ISMM ?12, Beijing, China, June 15-16, 2012. > > > > http://users.cecs.anu.edu.au/~steveb/downloads/pdf/rc-ismm-2012.pdf > > I think they give up deterministic finalization, which would make this > approach not suitable to Rust. > What do you mean exactly by deterministic finalization ? (I think I understand but my answer may be off) I think there are two different scenarios for determinism: - a stack-allocated object, or an object referred to by a unique reference (~) can easily be deterministically cleaned up (end of the scope) - an object referred to by a shared reference can be deterministically cleaned up only with accurate reference counting *and* in the absence of cycles AFAIK the latter (absence of cycles) is not merely theoretic, for example in Python, there is no guarantee that the __del__ method will be called for an object in the situation of cycles because the GC has to "break" the cycle at some point and, I think, "sacrifices" at least one object to do so. The article touches on statistic analysis to prove that an object, given its type, cannot be part of a cycle (ie, it cannot maintain shared references to other instances of its own type) however this can get impossible with ifaces: they form a compilation firewall. Still, the one solution to get deterministic finalization would be to forbid shared references to objects with an explicit destructor action if it cannot be statically proven they will not be part of a cycle. Or at least give a warning in this (hairy) case. I don't know how frequent it'll get. --- Moving on, the article has been written in the context of Java, which does not give any thought to immutability and has a much weaker type system. Immutability has a profound effect on references: immutable objects can only form a DAG! This alleviates the *one* big drawback of reference counting: no cycles of references means that there is no need for fallback strategies. Of course, as the article outlines, it might be worth having a tracing collection over young objects anyway. On the other hand, I do not know how well immutability deal with mutable counters embedded in the object. One of the selling point of immutability is referential transparency and the ease of sharing between concurrent (and thus possibly parallel) tasks. However this mutable counter means that at the bit-level the memory is not frozen, making sharing more difficult. I am not immersed enough in Rust: does Rust has a header for objects like Java does ? (I think not...) Furthermore, Rust's give emphasis to several pointer types, and the fact that `&` has no ownership associated means that it forms a natural barrier to reference counting. A function manipulating a `&str` does not have to worry about its ownership, it knows the object will outlive its call. This effectively removes lots of inc/dec operations. In Java this can only be inferred with escape analysis, and I don't know how much they rely on it or can actually infer with it. As a consequence, I am unsure of the impact this article should have on Rust's GC design. The implementation strategies presented are very clear and the advantages/drawbacks clearly outlined, which is great (big thank you to the OP); however the benchmarks and conclusions might be a tad Java-centric and not really apply to Rust's more advanced type system. --- Finally I think it might be worth considering having two distinct GC strategies: - one for immutable objects (that only references other immutable objects) - one for the rest (mutable objects with potential cycles) I see no reason to try and employ the same strategy for such widely different profiles other than the potential saving in term of coding effort. But then trying to cram every single usecase in a "generic" algorithm while keeping it efficient seems quite difficult too, whereas having several specialized mechanisms might make for much clearer code. One idea I have toyed with for my own was to have simple stubs: design a clear API for GC, with two (or more) sets of functions for example here, and call those functions instead of inlining their effect (in the IR). By providing the functions definitions externally (but inlining them in each IR module) this makes it easy to switch back and forth between various implementations whilst still retaining the efficiency of the LLVM backend to inline/optimize the calls. This means one can actually *test* the strategies, and perhaps even let the user *choose* which one better suits her needs. Of course coherency at the executable level might be necessary. -- Matthieu -------------- next part -------------- An HTML attachment was scrubbed... URL: From pwalton at mozilla.com Tue May 1 07:32:32 2012 From: pwalton at mozilla.com (Patrick Walton) Date: Tue, 01 May 2012 07:32:32 -0700 Subject: [rust-dev] Interesting paper on RC vs GC In-Reply-To: <87fwbk8hh0.fsf@mid.deneb.enyo.de> References: <87fwbk8hh0.fsf@mid.deneb.enyo.de> Message-ID: <4F9FF400.9070502@mozilla.com> On 05/01/2012 12:53 AM, Florian Weimer wrote: > * Sebastian Sylvan: > >> R. Shahriyar, S. M. Blackburn, and D. Frampton, "Down for the Count? >> Getting Reference Counting Back in the Ring," in Proceedings of the >> Eleventh ACM SIGPLAN International Symposium on Memory Management, >> ISMM ?12, Beijing, China, June 15-16, 2012. >> >> http://users.cecs.anu.edu.au/~steveb/downloads/pdf/rc-ismm-2012.pdf > > I think they give up deterministic finalization, which would make this > approach not suitable to Rust. Do you mean destroying an object the moment the last reference to it drops? If so, that's not a hard requirement in Rust's case. Patrick From sebastian.sylvan at gmail.com Tue May 1 08:51:15 2012 From: sebastian.sylvan at gmail.com (Sebastian Sylvan) Date: Tue, 1 May 2012 08:51:15 -0700 Subject: [rust-dev] Interesting paper on RC vs GC In-Reply-To: References: <87fwbk8hh0.fsf@mid.deneb.enyo.de> Message-ID: On Tue, May 1, 2012 at 4:07 AM, Matthieu Monrocq wrote: > As a consequence, I am unsure of the impact this article should have on > Rust's GC design. The implementation strategies presented are very clear and > the advantages/drawbacks clearly outlined, which is great (big thank you to > the OP); however the benchmarks and conclusions might be a tad Java-centric > and not really apply to Rust's more advanced type system. > My conjecture is that Java is *especially* unsuitable for RC for the following reasons: * lots of references, thus lots of reference traffic, thus lots of ref count inc/dec. * lots of garbage, thus more expensive for an algorithm for which the cost is proportional to the amount of garbage (RC) rather than to the amount of heap (GC). So I'd expect vanilla RC to do better in comparison to GC (though perhaps not beat it) in Rust than in Java. Applying the optimizations mentioned in the article (most of which rely on using deferred ref counting, which does mean you give up on predictable timing for deallocations) may make RC significantly better in Rust. Seb > --- > > Finally I think it might be worth considering having two distinct GC > strategies: > - one for immutable objects (that only references other immutable objects) > - one for the rest (mutable objects with potential cycles) > > I see no reason to try and employ the same strategy for such widely > different profiles other than the potential saving in term of coding effort. > But then trying to cram every single usecase in a "generic" algorithm while > keeping it efficient seems quite difficult too, whereas having several > specialized mechanisms might make for much clearer code. > > One idea I have toyed with for my own was to have simple stubs: design a > clear API for GC, with two (or more) sets of functions for example here, and > call those functions instead of inlining their effect (in the IR). By > providing the functions definitions externally (but inlining them in each IR > module) this makes it easy to switch back and forth between various > implementations whilst still retaining the efficiency of the LLVM backend to > inline/optimize the calls. > > This means one can actually *test* the strategies, and perhaps even let the > user *choose* which one better suits her needs. Of course coherency at the > executable level might be necessary. > > -- Matthieu -- Sebastian Sylvan From matthieu.monrocq at gmail.com Tue May 1 08:59:28 2012 From: matthieu.monrocq at gmail.com (Matthieu Monrocq) Date: Tue, 1 May 2012 17:59:28 +0200 Subject: [rust-dev] Interesting paper on RC vs GC In-Reply-To: References: <87fwbk8hh0.fsf@mid.deneb.enyo.de> Message-ID: On Tue, May 1, 2012 at 5:51 PM, Sebastian Sylvan wrote: > On Tue, May 1, 2012 at 4:07 AM, Matthieu Monrocq > wrote: > > As a consequence, I am unsure of the impact this article should have on > > Rust's GC design. The implementation strategies presented are very clear > and > > the advantages/drawbacks clearly outlined, which is great (big thank you > to > > the OP); however the benchmarks and conclusions might be a tad > Java-centric > > and not really apply to Rust's more advanced type system. > > > > My conjecture is that Java is *especially* unsuitable for RC for the > following reasons: > * lots of references, thus lots of reference traffic, thus lots of ref > count inc/dec. > * lots of garbage, thus more expensive for an algorithm for which the > cost is proportional to the amount of garbage (RC) rather than to the > amount of heap (GC). > > So I'd expect vanilla RC to do better in comparison to GC (though > perhaps not beat it) in Rust than in Java. Applying the optimizations > mentioned in the article (most of which rely on using deferred ref > counting, which does mean you give up on predictable timing for > deallocations) may make RC significantly better in Rust. > > Seb > > I agree that the technics outlined, especially with the details on their advantages/drawbacks are a very interesting read. As for the predictable timing, anyway it seems hard to have something predictable when you take cycle of references into account: I do not know any inexpensive algorithm to realize that by removing a link you are suddenly creating a self-sustaining group of objects that should be collected. Therefore I would venture that anyway such groups would be collected in a deferred fashion (using some tracing algorithm). -- Matthieu > > --- > > > > Finally I think it might be worth considering having two distinct GC > > strategies: > > - one for immutable objects (that only references other immutable > objects) > > - one for the rest (mutable objects with potential cycles) > > > > I see no reason to try and employ the same strategy for such widely > > different profiles other than the potential saving in term of coding > effort. > > But then trying to cram every single usecase in a "generic" algorithm > while > > keeping it efficient seems quite difficult too, whereas having several > > specialized mechanisms might make for much clearer code. > > > > One idea I have toyed with for my own was to have simple stubs: design a > > clear API for GC, with two (or more) sets of functions for example here, > and > > call those functions instead of inlining their effect (in the IR). By > > providing the functions definitions externally (but inlining them in > each IR > > module) this makes it easy to switch back and forth between various > > implementations whilst still retaining the efficiency of the LLVM > backend to > > inline/optimize the calls. > > > > This means one can actually *test* the strategies, and perhaps even let > the > > user *choose* which one better suits her needs. Of course coherency at > the > > executable level might be necessary. > > > > -- Matthieu > > > > -- > Sebastian Sylvan > -------------- next part -------------- An HTML attachment was scrubbed... URL: From banderson at mozilla.com Tue May 1 10:40:58 2012 From: banderson at mozilla.com (Brian Anderson) Date: Tue, 01 May 2012 10:40:58 -0700 Subject: [rust-dev] Strange info inside output In-Reply-To: <0A6DA044-E8D8-48A6-B4D4-47B7680D483A@gmail.com> References: <0A6DA044-E8D8-48A6-B4D4-47B7680D483A@gmail.com> Message-ID: <4FA0202A.2050702@mozilla.com> On 04/30/2012 11:44 PM, Alexander Stavonin wrote: > Am I right that this information is type of memory leak report? If it is true, is it possible to find time of memory allocation? > > Information example: > > ---------> <--------- (28 bytes from 0x7fbb9b415320) > +0 +4 +8 +c 0 4 8 c > +0000 1c 1e 1f 99 00 00 00 00 00 00 00 00 00 00 00 00 ................ > +0010 00 00 00 00 00 00 00 01 00 00 00 00 ............ > ---------> <--------- (16 bytes from 0x7fbb9b4148e0) > +0 +4 +8 +c 0 4 8 c > +0000 10 02 1f 99 7f 00 00 01 00 00 00 00 00 00 00 00 ................ > ---------> <--------- (28 bytes from 0x7fbb9b4152d0) > +0 +4 +8 +c 0 4 8 c > +0000 1c 1e 1f 99 00 00 00 00 fe 80 00 00 00 00 00 00 ................ > +0010 00 00 00 00 00 00 00 01 01 00 00 00 ............ > I don't recognize this. From graydon at mozilla.com Tue May 1 10:46:21 2012 From: graydon at mozilla.com (Graydon Hoare) Date: Tue, 01 May 2012 10:46:21 -0700 Subject: [rust-dev] Strange info inside output In-Reply-To: <0A6DA044-E8D8-48A6-B4D4-47B7680D483A@gmail.com> References: <0A6DA044-E8D8-48A6-B4D4-47B7680D483A@gmail.com> Message-ID: <4FA0216D.1020909@mozilla.com> On 12-04-30 11:44 PM, Alexander Stavonin wrote: > Am I right that this information is type of memory leak report? If it is true, is it possible to find time of memory allocation? Could you be more specific? Did a rust program print this output? If so, which program? I've never seen it before. -Graydon From pwalton at mozilla.com Tue May 1 11:57:16 2012 From: pwalton at mozilla.com (Patrick Walton) Date: Tue, 01 May 2012 11:57:16 -0700 Subject: [rust-dev] Interesting paper on RC vs GC In-Reply-To: References: <87fwbk8hh0.fsf@mid.deneb.enyo.de> Message-ID: <4FA0320C.8020902@mozilla.com> On 5/1/12 8:59 AM, Matthieu Monrocq wrote: > I agree that the technics outlined, especially with the details on their > advantages/drawbacks are a very interesting read. > > As for the predictable timing, anyway it seems hard to have something > predictable when you take cycle of references into account: I do not > know any inexpensive algorithm to realize that by removing a link you > are suddenly creating a self-sustaining group of objects that should be > collected. Therefore I would venture that anyway such groups would be > collected in a deferred fashion (using some tracing algorithm). Agreed. The problem with reference counting in the long term isn't the reference counting itself; it's the cycle collection. Patrick From banderson at mozilla.com Tue May 1 16:57:25 2012 From: banderson at mozilla.com (Brian Anderson) Date: Tue, 01 May 2012 16:57:25 -0700 Subject: [rust-dev] Testing facility and shared data In-Reply-To: <9881C0EA-AFBF-424F-9941-98FEB1233C81@gmail.com> References: <9881C0EA-AFBF-424F-9941-98FEB1233C81@gmail.com> Message-ID: <4FA07865.9040704@mozilla.com> On 05/01/2012 01:03 AM, Alexander Stavonin wrote: > I'm looking for a way for sharing data between all tests in a crate. Shared data or singletons are banned by Rust ideology, isn't it? Also we can not have single initialization point for all test. What is a best way to provide some common data for all tests in a crate or module? I don't have a good answer. There is no easy way to do this right now, and my response is mostly going to avoid the question. One very nice thing about our current setup is that the memory isolation helps make tests repeatable. Adding more external state to tests creates more ways for them to fail. So here are some things that are difficult to do with the current setup 1) using a common data setup for a set of tests 2) using state that globally requires a single initialization or shutdown 3) testing things that are not threadsafe For solving 1, a feature that might help is test fixtures with setup and teardown routines, like so many unit test frameworks. I strongly want to leave that kind of thing up to external frameworks that build upon the minimal std::test API though. In Rust a simple interface for fixtures might allow it to be used like #[test] fn mytest() { // `with_fixture` manages the setup and teardown with_fixture:: {|fixture| // use my fixture object assert fixture.initial_value == false; ... } } I've run into the second and third situation when testing bindings to native libraries that are not thread safe. In those cases I've just stuffed every affected test into a single test, but that's not a good long-term solution. The core library is currently growing functions to create global, singleton tasks for special internal purposes. Eventually, there will probably be a general solution for synchronizing access to global resources. From a.stavonin at gmail.com Tue May 1 17:18:56 2012 From: a.stavonin at gmail.com (Alexander Stavonin) Date: Wed, 2 May 2012 09:18:56 +0900 Subject: [rust-dev] Testing facility and shared data In-Reply-To: <4FA07865.9040704@mozilla.com> References: <9881C0EA-AFBF-424F-9941-98FEB1233C81@gmail.com> <4FA07865.9040704@mozilla.com> Message-ID: Unfortunately, I didn't caught your idea with with_fixture :( I have next situation, may be you have an idea how to fix it: I need to allocate unique TCP ports numbers for each test. In case of C/C++ solution is extreamly easy: create singleton or static variable and just atomically increase last_used_port value for each test. Do you have idea how to make it in case of Rust? I thought about external C library for storing static data, but it is dirty hack. May be would better to use singleton tasks? Could you point me into singleton tasks inside core library? -------------- next part -------------- An HTML attachment was scrubbed... URL: From niko at alum.mit.edu Tue May 1 18:35:11 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Tue, 01 May 2012 18:35:11 -0700 Subject: [rust-dev] Testing facility and shared data In-Reply-To: References: <9881C0EA-AFBF-424F-9941-98FEB1233C81@gmail.com> <4FA07865.9040704@mozilla.com> Message-ID: <4FA08F4F.602@alum.mit.edu> It is possible that the built-in test facility is not for you. You may want to create your own harness. Niko On 5/1/12 5:18 PM, Alexander Stavonin wrote: > Unfortunately, I didn't caught your idea with with_fixture :( I have > next situation, may be you have an idea how to fix it: > I need to allocate unique TCP ports numbers for each test. In case of > C/C++ solution is extreamly easy: create singleton or static variable > and just atomically increase last_used_port value for each test. Do > you have idea how to make it in case of Rust? I thought about external > C library for storing static data, but it is dirty hack. May be would > better to use singleton tasks? Could you point me into singleton tasks > inside core library? > > > _______________________________________________ > Rust-dev mailing list > Rust-dev at mozilla.org > https://mail.mozilla.org/listinfo/rust-dev From banderson at mozilla.com Tue May 1 20:02:31 2012 From: banderson at mozilla.com (Brian Anderson) Date: Tue, 1 May 2012 20:02:31 -0700 (PDT) Subject: [rust-dev] Testing facility and shared data In-Reply-To: Message-ID: <1217719921.908265.1335927751488.JavaMail.root@mozilla.com> ----- Original Message ----- > From: "Alexander Stavonin" > To: "Brian Anderson" > Cc: rust-dev at mozilla.org > Sent: Tuesday, May 1, 2012 5:18:56 PM > Subject: Re: [rust-dev] Testing facility and shared data > > Unfortunately, I didn't caught your idea with with_fixture :( I have > next situation, may be you have an idea how to fix it: > > I need to allocate unique TCP ports numbers for each test. In case of > C/C++ solution is extreamly easy: create singleton or static > variable and just atomically increase last_used_port value for each > test. Do you have idea how to make it in case of Rust? I thought > about external C library for storing static data, but it is dirty > hack. May be would better to use singleton tasks ? Could you point > me into singleton tasks inside core library? OK. Here are some short-term options. They are all bad. 1) If you only need the unique port numbers to avoid collisions when running in parallel then we can come up with a way to run the tests in serial, allowing you to reuse port numbers. A hack to do this is by setting the RUST_THREADS environment variable to 1. When there is only 1 scheduler thread the test runner will run tests serially. There may be other ways to achieve this kind without too much work. 2) If you just need to generate a unique integer in every test then write `unsafe::reinterpret_cast(task::get_task_id())`. This is a huge hack, but task id's are unique, pointer-sized, unsigned integers that start at 0 and increase monotonically. If you need many unique uints per task then do the same thing but use channels instead of tasks. 3) We can come up with a public API to create global, singleton tasks, possibly with the following signatures: unsafe fn register_named_service(n: str, f: fn~(port)) unsafe fn get_named_service(n: str) -> chan This would, if the named service doesn't exist, create a new task and execute the specified function. You would use this in each test to create or retrieve a global service to manage your state. The big reservation I have about this is that we can not currently make this interface type safe - we can't even check the types at runtime. -Brian From dpreston at mozilla.com Tue May 1 20:31:15 2012 From: dpreston at mozilla.com (Donovan Preston) Date: Tue, 01 May 2012 20:31:15 -0700 Subject: [rust-dev] Testing facility and shared data In-Reply-To: <1217719921.908265.1335927751488.JavaMail.root@mozilla.com> References: <1217719921.908265.1335927751488.JavaMail.root@mozilla.com> Message-ID: <4FA0AA83.50501@mozilla.com> Also, you can just listen on port 0 and the os will pick a random, unused port for you. On Tue May 1 20:02:31 2012, Brian Anderson wrote: > ----- Original Message ----- >> From: "Alexander Stavonin" >> To: "Brian Anderson" >> Cc: rust-dev at mozilla.org >> Sent: Tuesday, May 1, 2012 5:18:56 PM >> Subject: Re: [rust-dev] Testing facility and shared data >> >> Unfortunately, I didn't caught your idea with with_fixture :( I have >> next situation, may be you have an idea how to fix it: >> >> I need to allocate unique TCP ports numbers for each test. In case of >> C/C++ solution is extreamly easy: create singleton or static >> variable and just atomically increase last_used_port value for each >> test. Do you have idea how to make it in case of Rust? I thought >> about external C library for storing static data, but it is dirty >> hack. May be would better to use singleton tasks ? Could you point >> me into singleton tasks inside core library? > > OK. Here are some short-term options. They are all bad. > > 1) If you only need the unique port numbers to avoid collisions when running in parallel then we can come up with a way to run the tests in serial, allowing you to reuse port numbers. A hack to do this is by setting the RUST_THREADS environment variable to 1. When there is only 1 scheduler thread the test runner will run tests serially. There may be other ways to achieve this kind without too much work. > > 2) If you just need to generate a unique integer in every test then write `unsafe::reinterpret_cast(task::get_task_id())`. This is a huge hack, but task id's are unique, pointer-sized, unsigned integers that start at 0 and increase monotonically. If you need many unique uints per task then do the same thing but use channels instead of tasks. > > 3) We can come up with a public API to create global, singleton tasks, possibly with the following signatures: > > unsafe fn register_named_service(n: str, f: fn~(port)) > unsafe fn get_named_service(n: str) -> chan > > This would, if the named service doesn't exist, create a new task and execute the specified function. You would use this in each test to create or retrieve a global service to manage your state. The big reservation I have about this is that we can not currently make this interface type safe - we can't even check the types at runtime. > > -Brian > _______________________________________________ > Rust-dev mailing list > Rust-dev at mozilla.org > https://mail.mozilla.org/listinfo/rust-dev From a.stavonin at gmail.com Tue May 1 20:46:49 2012 From: a.stavonin at gmail.com (Alexander Stavonin) Date: Wed, 2 May 2012 12:46:49 +0900 Subject: [rust-dev] Testing facility and shared data In-Reply-To: <1217719921.908265.1335927751488.JavaMail.root@mozilla.com> References: <1217719921.908265.1335927751488.JavaMail.root@mozilla.com> Message-ID: I suppose that singleton tasks is the only way in this case. At least because of current testing facilities are usefull just for unit testing, but not for complex module testing, and in case of complex test cases we need to share some information between components. Also it will be needed in real-life application (for example for logging, security management and etc.). But, that type of API is not implemented yet, isn't it? Alexander. -------------- next part -------------- An HTML attachment was scrubbed... URL: From niko at alum.mit.edu Tue May 1 20:55:26 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Tue, 01 May 2012 20:55:26 -0700 Subject: [rust-dev] Testing facility and shared data In-Reply-To: <1217719921.908265.1335927751488.JavaMail.root@mozilla.com> References: <1217719921.908265.1335927751488.JavaMail.root@mozilla.com> Message-ID: <4FA0B02E.3080103@alum.mit.edu> On 5/1/12 8:02 PM, Brian Anderson wrote: > 3) We can come up with a public API to create global, singleton tasks, possibly with the following signatures: > > unsafe fn register_named_service(n: str, f: fn~(port)) > unsafe fn get_named_service(n: str) -> chan > > This would, if the named service doesn't exist, create a new task and execute the specified function. You would use this in each test to create or retrieve a global service to manage your state. The big reservation I have about this is that we can not currently make this interface type safe - we can't even check the types at runtime. So, brson and I were discussing this earlier. This was the most type safe pattern I could come up with (it's still flawed): enum named_service_key = { _named_service_key(str) // not exported } unsafe fn create_named_service_key(n: str) -> named_service_key { _named_service_key(n) } fn register_named_service(n: named_service_key, f: fn~(port)) fn get_named_service(n: named_service_key) -> chan then to create a named service you would do something like: fn random_int_service_key() -> named_service_key<()> { unsafe{create_named_service_key::<()>("random")} } Now at least the types are not provided anew at each call to `get_named_service()` but rather they are specified when the named service key is created. I use an unsafe fn to make the key itself since that's the point where the type of channels is specified and that will ultimately be unchecked. But this all seems clumsy. Niko From niko at alum.mit.edu Thu May 3 16:05:41 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Thu, 03 May 2012 16:05:41 -0700 Subject: [rust-dev] omitting argument types in fn exprs Message-ID: <4FA30F45.3050703@alum.mit.edu> Hello, I have a patch [1] to permit anonymous functions to omit their argument types. For example: vec.iter(fn@(x) {...}) This works fine but it occurs to me it is unclear what to do with the return type. In particular, does `fn@(x) { ... }` return unit, or is the return type inferred? Also, it occurs to me that I ought to check whether there is any objection to such a feature. I've never heard any objections, but I haven't asked explicitly on the list. There is an open bug (https://github.com/mozilla/rust/issues/2093). Niko [1] https://github.com/mozilla/rust/compare/6e5c8a7...1ba4ca4 From niko at alum.mit.edu Thu May 3 21:49:27 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Thu, 03 May 2012 21:49:27 -0700 Subject: [rust-dev] capture clause syntax Message-ID: <4FA35FD7.4080001@alum.mit.edu> I'm trying to knock off some small bugs that have been lingering for a while. One of them is a change to the "capture clause" syntax, as described here: https://github.com/mozilla/rust/issues/2096 Basically the capture clause becomes integrated into the parameter list. The main motivation for this is that it integrates well with the sugared closure syntax. So you can write: let x = ~3; task::spawn { |move x| // use x in here } In effect, a "parameter" that looks like `copy x` or `move y` copies/moves the local variable out of the environment into the closure. It's also good for multi-line function definitions, I think: let a_fn = fn@( x: T1, y: T2, copy w, move z) { ... }; I implemented this but wanted to do a final check before pushing to see if anyone had any objections. Niko From graydon at mozilla.com Fri May 4 10:16:27 2012 From: graydon at mozilla.com (Graydon Hoare) Date: Fri, 04 May 2012 10:16:27 -0700 Subject: [rust-dev] capture clause syntax In-Reply-To: <4FA35FD7.4080001@alum.mit.edu> References: <4FA35FD7.4080001@alum.mit.edu> Message-ID: <4FA40EEB.4010103@mozilla.com> On 12-05-03 09:49 PM, Niko Matsakis wrote: > So you can write: > > let x = ~3; > task::spawn { |move x| > // use x in here > } > ... > I implemented this but wanted to do a final check before pushing to see > if anyone had any objections. I think this is as good as any, yeah. Thanks for grinding down these little corners. The details do count :) -Graydon From graydon at mozilla.com Fri May 4 11:06:40 2012 From: graydon at mozilla.com (Graydon Hoare) Date: Fri, 04 May 2012 11:06:40 -0700 Subject: [rust-dev] Testing facility and shared data In-Reply-To: <4FA0B02E.3080103@alum.mit.edu> References: <1217719921.908265.1335927751488.JavaMail.root@mozilla.com> <4FA0B02E.3080103@alum.mit.edu> Message-ID: <4FA41AB0.2040409@mozilla.com> On 12-05-01 08:55 PM, Niko Matsakis wrote: > But this all seems clumsy. Yeah. I think this is a straightforward argument in favour of https://github.com/mozilla/rust/issues/553 (globals) I've made some further notes there, I think it's the right place to have the conversation. This facility is becoming more pressing the more we do serious stuff with the language, I think it ought to be a 0.3 or 0.4 priority. -Graydon From harrij3 at tigermail.auburn.edu Fri May 4 10:56:59 2012 From: harrij3 at tigermail.auburn.edu (John Harrison) Date: Fri, 4 May 2012 17:56:59 +0000 Subject: [rust-dev] That misunderstood C type qualifier: volatile Message-ID: <87177C11-6D2D-416E-A740-42FD7594F675@tigermail.auburn.edu> Greetings all, I found out about rust a while back and have been toying with the idea of trying to use it on an embedded device. In an ideal world, I think it would be nice to use rust on say the Raspberry Pi [1] or an mbed [2]. I have used clang to compile code for the mbed before, so I know the code generation components will get there eventually. However, one aspect of embedded programming that would be difficult to do currently with rust is accessing memory mapped I/O (MMIO) pins and accessing values that can be manipulated in a signal. I understand that most people do not understand the volatile keyword and it is very commonly misused, but it does serve a very important purpose in embedded and low level programming. I could always try to write anything that requires MMIO or signals in C and just call it from rust, but that feels unsatisfactory. So I was wondering if this seems like a valid use case for rust or is it out of the scope of the current purpose of the language? -- John Harrison 1. http://www.raspberrypi.org 2. http://mbed.org From arcata at gmail.com Fri May 4 11:58:30 2012 From: arcata at gmail.com (Joe Groff) Date: Fri, 4 May 2012 11:58:30 -0700 Subject: [rust-dev] That misunderstood C type qualifier: volatile In-Reply-To: <87177C11-6D2D-416E-A740-42FD7594F675@tigermail.auburn.edu> References: <87177C11-6D2D-416E-A740-42FD7594F675@tigermail.auburn.edu> Message-ID: On Fri, May 4, 2012 at 10:56 AM, John Harrison wrote: > I understand that most people do not understand the volatile keyword and it is very commonly misused, but it does serve a very important purpose in embedded and low level programming. I could always try to write anything that requires MMIO or signals in C and just call it from rust, but that feels unsatisfactory. An easy stopgap might be to add unsafe primitives for volatile_load and volatile_store that map to LLVM load volatile/store volatile instructions. -Joe From arcata at gmail.com Fri May 4 13:36:59 2012 From: arcata at gmail.com (Joe Groff) Date: Fri, 4 May 2012 13:36:59 -0700 Subject: [rust-dev] That misunderstood C type qualifier: volatile In-Reply-To: References: <87177C11-6D2D-416E-A740-42FD7594F675@tigermail.auburn.edu> Message-ID: On Fri, May 4, 2012 at 1:09 PM, John Harrison wrote: > Which is not that different, just more explicit, but again I do not know how the optimizer would interact with that kind of a statement. It would be nice to be able to mark an expression as volatile possibly instead of a variable. It should work fine. In LLVM volatile operations are marked at the load/store level, so it would map to the same IR as volatile accesses in C would. (LLVM's semantics for volatile operations is documented at http://llvm.org/docs/LangRef.html#volatile , if you're curious.) Even in C, I've seen a lot of code that makes volatile load/stores explicit at the site of the operation with operations of the form 'int x; *(volatile int*)&x = 1;' in favor of using volatile typed variables. If you want the type safety of a volatile type in Rust, you could define a volatile enum that only allowed access using methods defined in terms of the volatile primitives. -Joe From arcata at gmail.com Fri May 4 14:09:34 2012 From: arcata at gmail.com (Joe Groff) Date: Fri, 4 May 2012 14:09:34 -0700 Subject: [rust-dev] That misunderstood C type qualifier: volatile In-Reply-To: <27C3661F-67A8-4845-8828-480A40491B75@tigermail.auburn.edu> References: <87177C11-6D2D-416E-A740-42FD7594F675@tigermail.auburn.edu> <27C3661F-67A8-4845-8828-480A40491B75@tigermail.auburn.edu> Message-ID: On Fri, May 4, 2012 at 1:51 PM, John Harrison wrote: > I know the llvm-ir has constructs for volatile but I was referring to any optimizations done above the llvm-ir, such as at the AST level. I am not sure what kinds of optimizations Rust currently does, so this might not be an issue. Good point. One of the core devs would be able to answer for sure, but I think that by virtue of being an unsafe function operating on an unsafe pointer, a volatile_load function should be an optimization barrier relative to the value pointed to by the pointer. I don't think the language can assume anything about the pointee after an unsafe call. One thing I was wondering?don't C11's atomics mostly supersede the use of volatile for shared-memory and MMIO? Volatile's guarantees are pretty weak and are only really sufficient for setjmp/longjmp and (in POSIX) sig_atomic_t on modern architectures. Embedded toolchains may of course have stronger platform-specific guarantees for volatile, but the atomic operations would be more portable and generally useful. -Joe From harrij3 at tigermail.auburn.edu Fri May 4 13:09:30 2012 From: harrij3 at tigermail.auburn.edu (John Harrison) Date: Fri, 4 May 2012 20:09:30 +0000 Subject: [rust-dev] That misunderstood C type qualifier: volatile In-Reply-To: References: <87177C11-6D2D-416E-A740-42FD7594F675@tigermail.auburn.edu> Message-ID: Ya, it could be possible to implement a few bits of this as assembly or primitive llvm instructions. My only worry would be how the optimizer interacts with some of those statements. If it sees what it thinks is an infinite loop it might optimize out something important, I haven't invested to much time into this yet as I do not think the runtime works on ARM platforms yet, I know a few bits of rust are still actively changing (like vectors and the addition of classes). It is something I would be interested in looking into in the future however. The common case in embedded is something like: int PIN1 = address 0x4000; // MMIO Pin while (1) { if (PIN1 == HIGH) { do_something(); } } And if the value of the PIN1 is set to 0/1 initially and not set as volatile then a compiler can recognize that the value will never change, so the if's body is dead code. With an explicit load instruction you can do something like: int PIN1 = address 0x4000; // MMIO Pin while (1) { val pin1_state = volatile_load(PIN1); if (pin1_state == HIGH) { do_something; } } Which is not that different, just more explicit, but again I do not know how the optimizer would interact with that kind of a statement. It would be nice to be able to mark an expression as volatile possibly instead of a variable. Possible something like: let PIN1: address = 0x4000; loop { if volatile *PIN1 == 1 { do_something; } } This could be done entirely in llvm-ir or assembly, so maybe I should just argue for an asm/llvm-ir syntax extension. -- John Harrison On May 4, 2012, at 11:58 AM, Joe Groff wrote: > On Fri, May 4, 2012 at 10:56 AM, John Harrison > wrote: >> I understand that most people do not understand the volatile keyword and it is very commonly misused, but it does serve a very important purpose in embedded and low level programming. I could always try to write anything that requires MMIO or signals in C and just call it from rust, but that feels unsatisfactory. > > An easy stopgap might be to add unsafe primitives for volatile_load > and volatile_store that map to LLVM load volatile/store volatile > instructions. > > -Joe > From harrij3 at tigermail.auburn.edu Fri May 4 13:51:59 2012 From: harrij3 at tigermail.auburn.edu (John Harrison) Date: Fri, 4 May 2012 20:51:59 +0000 Subject: [rust-dev] That misunderstood C type qualifier: volatile In-Reply-To: References: <87177C11-6D2D-416E-A740-42FD7594F675@tigermail.auburn.edu> Message-ID: <27C3661F-67A8-4845-8828-480A40491B75@tigermail.auburn.edu> I know the llvm-ir has constructs for volatile but I was referring to any optimizations done above the llvm-ir, such as at the AST level. I am not sure what kinds of optimizations Rust currently does, so this might not be an issue. > If you want the type safety of a volatile type in Rust, you could > define a volatile enum that only allowed access using methods > defined in terms of the volatile primitives. Ya, I was thinking about defining an enum/class/iface for this purpose. -- John Harrison On May 4, 2012, at 1:36 PM, Joe Groff wrote: > On Fri, May 4, 2012 at 1:09 PM, John Harrison > wrote: >> Which is not that different, just more explicit, but again I do not know how the optimizer would interact with that kind of a statement. It would be nice to be able to mark an expression as volatile possibly instead of a variable. > > It should work fine. In LLVM volatile operations are marked at the > load/store level, so it would map to the same IR as volatile accesses > in C would. (LLVM's semantics for volatile operations is documented at > http://llvm.org/docs/LangRef.html#volatile , if you're curious.) Even > in C, I've seen a lot of code that makes volatile load/stores explicit > at the site of the operation with operations of the form 'int x; > *(volatile int*)&x = 1;' in favor of using volatile typed variables. > > If you want the type safety of a volatile type in Rust, you could > define a volatile enum that only allowed access using methods > defined in terms of the volatile primitives. > > -Joe > From niko at alum.mit.edu Fri May 4 14:57:05 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Fri, 04 May 2012 14:57:05 -0700 Subject: [rust-dev] method calls vs closure calls Message-ID: <4FA450B1.2070504@alum.mit.edu> I had a crazy thought for how to make method call syntax unambiguously distinguishable from field access without making it ugly. In short: make `a.b(c, d)` *always* a method call, rather than parsing it as a call to a value stored in a field. If you actually wanted to call a closure in a field, you would make that explicit by writing `(a.b)(c, d)`. Thoughts? Background: Currently, the expression `a.b` is rather ambiguous. What we do is to check whether `a` has a field named `b`. If there is no such type, then we search for a method named `b`. This ambiguity is somewhat confusing at times (e.g., #2327). It also causes problems with having a field and a method with the same name, which is sometimes necessary to fulfill an iface etc. Niko From pwalton at mozilla.com Fri May 4 14:58:25 2012 From: pwalton at mozilla.com (Patrick Walton) Date: Fri, 04 May 2012 14:58:25 -0700 Subject: [rust-dev] method calls vs closure calls In-Reply-To: <4FA450B1.2070504@alum.mit.edu> References: <4FA450B1.2070504@alum.mit.edu> Message-ID: <4FA45101.10108@mozilla.com> On 5/4/12 2:57 PM, Niko Matsakis wrote: > I had a crazy thought for how to make method call syntax unambiguously > distinguishable from field access without making it ugly. > > In short: make `a.b(c, d)` *always* a method call, rather than parsing > it as a call to a value stored in a field. If you actually wanted to > call a closure in a field, you would make that explicit by writing > `(a.b)(c, d)`. > > Thoughts? I like this. It addresses the concern I had when proposing ->, without the ugliness of ->. Patrick From marijnh at gmail.com Sat May 5 02:27:07 2012 From: marijnh at gmail.com (Marijn Haverbeke) Date: Sat, 5 May 2012 11:27:07 +0200 Subject: [rust-dev] method calls vs closure calls In-Reply-To: <4FA450B1.2070504@alum.mit.edu> References: <4FA450B1.2070504@alum.mit.edu> Message-ID: How would one take the value of a method? I think Patrick's -> proposal, or some variation thereof, is preferable, in that it uses obviously different syntax for the different concepts, and is less likely to confuse people. From arcata at gmail.com Mon May 7 11:24:16 2012 From: arcata at gmail.com (Joe Groff) Date: Mon, 7 May 2012 11:24:16 -0700 Subject: [rust-dev] That misunderstood C type qualifier: volatile In-Reply-To: <5B201544-39D1-41CA-B410-E82BC83964D2@tigermail.auburn.edu> References: <87177C11-6D2D-416E-A740-42FD7594F675@tigermail.auburn.edu> <27C3661F-67A8-4845-8828-480A40491B75@tigermail.auburn.edu> <5B201544-39D1-41CA-B410-E82BC83964D2@tigermail.auburn.edu> Message-ID: On Mon, May 7, 2012 at 9:31 AM, John Harrison wrote: > And you can guarantee that access to foo are atomic. Really, most CPU's can load up to a double word in a single instruction, sometimes more. But for complex data structures they may have to break down the load into multiple operations and thats when atomic is important. Anyway, volatile is a different use case. Atomic operations will also emit any synchronization instructions necessary to ensure that the operation is observed outside of the CPU with the ordering specified by the atomic operation. The optimization barrier provided by volatile alone isn't enough to enable MMIO on modern architectures; the CPU and cache controller are still free to reorder or eliminate loads and stores treated as volatile by the compiler. -Joe From harrij3 at tigermail.auburn.edu Mon May 7 09:31:21 2012 From: harrij3 at tigermail.auburn.edu (John Harrison) Date: Mon, 7 May 2012 16:31:21 +0000 Subject: [rust-dev] That misunderstood C type qualifier: volatile In-Reply-To: References: <87177C11-6D2D-416E-A740-42FD7594F675@tigermail.auburn.edu> <27C3661F-67A8-4845-8828-480A40491B75@tigermail.auburn.edu> Message-ID: <5B201544-39D1-41CA-B410-E82BC83964D2@tigermail.auburn.edu> > One thing I was wondering?don't C11's atomics mostly supersede the use > of volatile for shared-memory and MMIO? Volatile's guarantees are > pretty weak and are only really sufficient for setjmp/longjmp and (in > POSIX) sig_atomic_t on modern architectures. Embedded toolchains may > of course have stronger platform-specific guarantees for volatile, but > the atomic operations would be more portable and generally useful. Volatile and atomic are for two different purposes. Volatile is for declaring that variable access cannot be optimized away and atomic has to do with how the CPU accesses the variable. Atomic variables can still be moved around by the optimizer and optimized away, while a volatile variable cannot. Atomic is about accessing a value that could be concurrently manipulated. So I think they are sufficiently different use cases. Before C11/C++11 it was sometimes difficult to determine if variable access could be interrupted. In embedded programming, for instance, say you have a 16-bit memory bus and your accessing a 64-bit value. That would require 4 loads to access each part of the value. It is possible, if interrupts are enabled, for the CPU to switch to an interrupt handler between the loads, thus the 64-bit value may be wrong if it was manipulated in the interrupt handler. However, if the value you were accessing was only a 16-bit value, that can occur in 1 instruction that would not be interruptible. To fix this situation you can declare the variable to be atomic. In C++11 you can say: std::atomic foo; And you can guarantee that access to foo are atomic. Really, most CPU's can load up to a double word in a single instruction, sometimes more. But for complex data structures they may have to break down the load into multiple operations and thats when atomic is important. Anyway, volatile is a different use case. Btw, I realized that clang/gcc allow you to say: `asm` and `asm volatile` to indicate whether or not to optimize the assembly instructions. -- John Harrison On May 4, 2012, at 2:09 PM, Joe Groff wrote: > On Fri, May 4, 2012 at 1:51 PM, John Harrison > wrote: >> I know the llvm-ir has constructs for volatile but I was referring to any optimizations done above the llvm-ir, such as at the AST level. I am not sure what kinds of optimizations Rust currently does, so this might not be an issue. > > Good point. One of the core devs would be able to answer for sure, but > I think that by virtue of being an unsafe function operating on an > unsafe pointer, a volatile_load function should be an optimization > barrier relative to the value pointed to by the pointer. I don't think > the language can assume anything about the pointee after an unsafe > call. > > One thing I was wondering?don't C11's atomics mostly supersede the use > of volatile for shared-memory and MMIO? Volatile's guarantees are > pretty weak and are only really sufficient for setjmp/longjmp and (in > POSIX) sig_atomic_t on modern architectures. Embedded toolchains may > of course have stronger platform-specific guarantees for volatile, but > the atomic operations would be more portable and generally useful. > > -Joe > From niko at alum.mit.edu Mon May 7 12:39:55 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Mon, 07 May 2012 12:39:55 -0700 Subject: [rust-dev] agenda Message-ID: <4FA8250B.7090600@alum.mit.edu> As we have oft discussed but never done, I created an etherpad for collecting agenda items in advance of the weekly meeting tomorrow: https://etherpad.mozilla.org/Meeting-weekly-2012-05-08 Niko From niko at alum.mit.edu Wed May 9 20:32:06 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Wed, 09 May 2012 20:32:06 -0700 Subject: [rust-dev] RFC: Removing `as` (a WIP) Message-ID: <4FAB36B6.50200@alum.mit.edu> The `as` operator is annoying for a number of reasons: - the precedence is non-obvious and hard to get right (3 + 4 as uint) - it introduces a weird ambiguity around type parameters (`3 as foo::`) Recently Go's syntax was proposed as a remedy (`3.(int)`), but rejected because it looked weird (which it does). But it occurs to me that we don't really use `as` for much. In fact, we use it for three things: converting between numeric forms, creating boxed ifaces, and converting C-like enums to their integer values. I have good alternative forms for the first two, not sure about the third. ### Converting between numeric forms We can simply create some impls for the appropriate conversions. For example: ``` impl conv_methods for int { fn uint() -> uint { sys::int_to_uint(self) } fn u32() -> u32 { unsafe::reinterpret_cast(self) } fn float() -> float { ... } } ``` These have the advantage of clear precedence and they're more attractive to boot (at least to me). Examples: - `3.uint() + 4u` - `(3+4).uint()` ### Creating boxed ifaces I've started to find it a bit odd to use `as` to create a boxed iface, since `as` looks like a "bitcast" (in LLVM terms) but it's actually performing allocation. It's also quite repetitive, especially if the iface has type parameters. Therefore, I propose we simply use the `iface` keyword to indicate "create a boxed iface". So, instead of this: foo as my_iface:: we would write iface(foo) The required iface and type parameters would typically be inferred from context, but if you wanted to make it fully explicit, you would do: iface::>(foo) This purposefully resembles a method call but of course `iface` is a keyword so it is not, in fact, a method call but rather a special form. *An aside:* I originally thought to use the name of the iface as the constructor for a boxed iface (e.g., `my_iface(foo)`) but I eventually rejected it because if we move to a system where ifaces can be allocated not only in `@` boxes but also on the stack and so forth, the `iface` keyword should be easily extensible to this scenario, whereas `my_iface(foo)` is not. This is true regardless of whether we use "suffixed types" (e.g., `iface@(foo)`, `iface~(foo)`) or move to a prefix-with-dynamically-sized-types system (e.g., `@iface(foo)`, `~iface(foo)`). Neither case can be gracefully handled by a named constructor function unless these functions are actually treated specially by the system. ### Converting C-like enums to their discriminant Actually, I don't have a good idea what to do here. The compiler could of course automatically define the relevant methods on C-like enums (`int`, `uint` and so on) but that seems hokey. Thoughts? Niko From niko at alum.mit.edu Wed May 9 21:23:59 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Wed, 09 May 2012 21:23:59 -0700 Subject: [rust-dev] passing by mutable ref Message-ID: <4FAB42DF.3010300@alum.mit.edu> I am looking at this test case run-pass/issue-511: fn f(&o: option) { assert o == option::none; } fn main() { f::(option::none); } Is this really supposed to pass? The bug (#511) suggests it is, but I guess the language has migrated some since then. In any case, if it is intended to be legal, what is the meaning of passing a global constant (option::none) by mutable reference? Also, in that case, we are not fully consistent. For example, this is not permitted: fn foo(&i: int) {} fn main() { foo(3); } Niko From marijnh at gmail.com Wed May 9 23:09:11 2012 From: marijnh at gmail.com (Marijn Haverbeke) Date: Thu, 10 May 2012 08:09:11 +0200 Subject: [rust-dev] passing by mutable ref In-Reply-To: <4FAB42DF.3010300@alum.mit.edu> References: <4FAB42DF.3010300@alum.mit.edu> Message-ID: I agree that this shouldn't be allowed. A nullary variant name is not an lvalue. From ben.striegel at gmail.com Thu May 10 05:20:02 2012 From: ben.striegel at gmail.com (Benjamin Striegel) Date: Thu, 10 May 2012 08:20:02 -0400 Subject: [rust-dev] RFC: Removing `as` (a WIP) In-Reply-To: <4FAB36B6.50200@alum.mit.edu> References: <4FAB36B6.50200@alum.mit.edu> Message-ID: > 3.uint() + 4u Does this parse correctly, or would you need to do (3).uint(), like in Javascript? On Wed, May 9, 2012 at 11:32 PM, Niko Matsakis wrote: > The `as` operator is annoying for a number of reasons: > > - the precedence is non-obvious and hard to get right (3 + 4 as uint) > - it introduces a weird ambiguity around type parameters (`3 as foo::`) > > Recently Go's syntax was proposed as a remedy (`3.(int)`), but rejected > because it looked weird (which it does). > > But it occurs to me that we don't really use `as` for much. In fact, we > use it for three things: converting between numeric forms, creating boxed > ifaces, and converting C-like enums to their integer values. I have good > alternative forms for the first two, not sure about the third. > > ### Converting between numeric forms > > We can simply create some impls for the appropriate conversions. For > example: > > ``` > impl conv_methods for int { > fn uint() -> uint { sys::int_to_uint(self) } > fn u32() -> u32 { unsafe::reinterpret_cast(self) } > fn float() -> float { ... } > } > ``` > > These have the advantage of clear precedence and they're more attractive > to boot (at least to me). Examples: > > - `3.uint() + 4u` > - `(3+4).uint()` > > ### Creating boxed ifaces > > I've started to find it a bit odd to use `as` to create a boxed iface, > since `as` looks like a "bitcast" (in LLVM terms) but it's actually > performing allocation. It's also quite repetitive, especially if the iface > has type parameters. Therefore, I propose we simply use the `iface` > keyword to indicate "create a boxed iface". So, instead of this: > > foo as my_iface:: > > we would write > > iface(foo) > > The required iface and type parameters would typically be inferred from > context, but if you wanted to make it fully explicit, you would do: > > iface::>(foo) > > This purposefully resembles a method call but of course `iface` is a > keyword so it is not, in fact, a method call but rather a special form. > > *An aside:* I originally thought to use the name of the iface as the > constructor for a boxed iface (e.g., `my_iface(foo)`) but I eventually > rejected it because if we move to a system where ifaces can be allocated > not only in `@` boxes but also on the stack and so forth, the `iface` > keyword should be easily extensible to this scenario, whereas > `my_iface(foo)` is not. This is true regardless of whether we use > "suffixed types" (e.g., `iface@(foo)`, `iface~(foo)`) or move to a > prefix-with-dynamically-sized-**types system (e.g., `@iface(foo)`, > `~iface(foo)`). Neither case can be gracefully handled by a named > constructor function unless these functions are actually treated specially > by the system. > > ### Converting C-like enums to their discriminant > > Actually, I don't have a good idea what to do here. The compiler could of > course automatically define the relevant methods on C-like enums (`int`, > `uint` and so on) but that seems hokey. > > Thoughts? > > > Niko > > ______________________________**_________________ > Rust-dev mailing list > Rust-dev at mozilla.org > https://mail.mozilla.org/**listinfo/rust-dev > -------------- next part -------------- An HTML attachment was scrubbed... URL: From niko at alum.mit.edu Thu May 10 06:53:11 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Thu, 10 May 2012 06:53:11 -0700 Subject: [rust-dev] RFC: Removing `as` (a WIP) In-Reply-To: References: <4FAB36B6.50200@alum.mit.edu> Message-ID: <4FABC847.4050907@alum.mit.edu> On 5/10/12 5:20 AM, Benjamin Striegel wrote: > > 3.uint() + 4u > > Does this parse correctly, or would you need to do (3).uint(), like in > Javascript? So, 3.foo() parses fine, but I'm not sure how -3.foo() parses, actually. Probably as -(3.foo()). Niko From graydon at mozilla.com Thu May 10 07:57:03 2012 From: graydon at mozilla.com (Graydon Hoare) Date: Thu, 10 May 2012 07:57:03 -0700 Subject: [rust-dev] RFC: Removing `as` (a WIP) In-Reply-To: <4FAB36B6.50200@alum.mit.edu> References: <4FAB36B6.50200@alum.mit.edu> Message-ID: <4FABD73F.3020604@mozilla.com> On 09/05/2012 8:32 PM, Niko Matsakis wrote: > The `as` operator is annoying for a number of reasons: > > - the precedence is non-obvious and hard to get right (3 + 4 as uint) > - it introduces a weird ambiguity around type parameters (`3 as foo::`) > > Recently Go's syntax was proposed as a remedy (`3.(int)`), but rejected > because it looked weird (which it does). I concur it'd be nice to get rid of the current form. I'm a little nervous about what you're proposing here. I think enums can probably be done with a sys::enum_value intrinsic that fails to compile on non-enums. Or something involving the reflection interface I'm currently building. I don't think this part is a blocker. The bigger concern I have is moving from an expression that's reasonably a const-expr to what looks like a function call. A few cases stand out (say "((0xff << 10) | 'z') as uint" or such) though possibly they can be solved by cramming more temporary consts and/or type-literal suffixes in. More generally it raises my concern about const-exprs as functions. Iintrinsics probably already have this problem; we won't constant-fold through them. It'll get particularly bad if we move (as you and I have discussed) to using the operator overload system for all the normal arithmetic forms; 'as' is then just a special case of "how do we get constant folding through user-defined expressions"? In C++0x they added "constexpr" as a qualifier for functions. I could imagine supporting 'const fn foo' in rust as a way of indicating a fn that can be used in a const-expr, at least when applied to const-expr arguments. (nb: even if const-folding through user expressions wind up turing-complete, it's not the same as "turing complete type system", and I'm much less concerned about it. We already support the concept of invoking arbitrary code at compile time, due to the plan for syntax extensions and attribute extensions. My objection to making the _type_ system turing complete is that the encoding ,"evaluation" model, type-system-of-types and diagnostic facilities are very very crude, so it's a bad place to enrich to that level.) So uh .. in the specific case of 'as' I'm not deeply worried what it changes to, if anything. But I think we need to look at constant folding first before making a move on it. Sorry if that seems like derailing your goal! -Graydon From niko at alum.mit.edu Thu May 10 10:13:47 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Thu, 10 May 2012 10:13:47 -0700 Subject: [rust-dev] RFC: Removing `as` (a WIP) In-Reply-To: <4FABD73F.3020604@mozilla.com> References: <4FAB36B6.50200@alum.mit.edu> <4FABD73F.3020604@mozilla.com> Message-ID: <4FABF74B.9040605@alum.mit.edu> That's a good point (as in const expressions). What do you think about the iface casting form? Niko On 5/10/12 7:57 AM, Graydon Hoare wrote: > On 09/05/2012 8:32 PM, Niko Matsakis wrote: >> The `as` operator is annoying for a number of reasons: >> >> - the precedence is non-obvious and hard to get right (3 + 4 as uint) >> - it introduces a weird ambiguity around type parameters (`3 as >> foo::`) >> >> Recently Go's syntax was proposed as a remedy (`3.(int)`), but rejected >> because it looked weird (which it does). > > I concur it'd be nice to get rid of the current form. > > I'm a little nervous about what you're proposing here. I think enums > can probably be done with a sys::enum_value intrinsic that fails to > compile on non-enums. Or something involving the reflection interface > I'm currently building. I don't think this part is a blocker. > > The bigger concern I have is moving from an expression that's > reasonably a const-expr to what looks like a function call. A few > cases stand out (say "((0xff << 10) | 'z') as uint" or such) though > possibly they can be solved by cramming more temporary consts and/or > type-literal suffixes in. > > More generally it raises my concern about const-exprs as functions. > Iintrinsics probably already have this problem; we won't constant-fold > through them. It'll get particularly bad if we move (as you and I have > discussed) to using the operator overload system for all the normal > arithmetic forms; 'as' is then just a special case of "how do we get > constant folding through user-defined expressions"? In C++0x they > added "constexpr" as a qualifier for functions. I could imagine > supporting 'const fn foo' in rust as a way of indicating a fn that can > be used in a const-expr, at least when applied to const-expr arguments. > > (nb: even if const-folding through user expressions wind up > turing-complete, it's not the same as "turing complete type system", > and I'm much less concerned about it. We already support the concept > of invoking arbitrary code at compile time, due to the plan for syntax > extensions and attribute extensions. My objection to making the _type_ > system turing complete is that the encoding ,"evaluation" model, > type-system-of-types and diagnostic facilities are very very crude, so > it's a bad place to enrich to that level.) > > So uh .. in the specific case of 'as' I'm not deeply worried what it > changes to, if anything. But I think we need to look at constant > folding first before making a move on it. > > Sorry if that seems like derailing your goal! > > -Graydon > _______________________________________________ > Rust-dev mailing list > Rust-dev at mozilla.org > https://mail.mozilla.org/listinfo/rust-dev From garethdanielsmith at gmail.com Thu May 10 10:56:25 2012 From: garethdanielsmith at gmail.com (Gareth Smith) Date: Thu, 10 May 2012 18:56:25 +0100 Subject: [rust-dev] RFC: Removing `as` (a WIP) In-Reply-To: <4FABD73F.3020604@mozilla.com> References: <4FAB36B6.50200@alum.mit.edu> <4FABD73F.3020604@mozilla.com> Message-ID: <4FAC0149.4070303@gmail.com> On 10/05/12 15:57, Graydon Hoare wrote: > > I'm a little nervous about what you're proposing here. I think enums > can probably be done with a sys::enum_value intrinsic that fails to > compile on non-enums. Or something involving the reflection interface > I'm currently building. I don't think this part is a blocker. Just a thought - how about `sys::enum_value(x: T) -> uint` where c_enum is some new kind given only to c-style enums? Gareth From garethdanielsmith at gmail.com Thu May 10 10:56:57 2012 From: garethdanielsmith at gmail.com (Gareth Smith) Date: Thu, 10 May 2012 18:56:57 +0100 Subject: [rust-dev] RFC: Removing `as` (a WIP) In-Reply-To: <4FABD73F.3020604@mozilla.com> References: <4FAB36B6.50200@alum.mit.edu> <4FABD73F.3020604@mozilla.com> Message-ID: <4FAC0169.1030109@gmail.com> On 10/05/12 15:57, Graydon Hoare wrote: > > I'm a little nervous about what you're proposing here. I think enums > can probably be done with a sys::enum_value intrinsic that fails to > compile on non-enums. Or something involving the reflection interface > I'm currently building. I don't think this part is a blocker. Just a thought - how about `sys::enum_value(x: T) -> uint` where c_enum is some new kind given only to c-style enums? Gareth From graydon at mozilla.com Thu May 10 11:22:20 2012 From: graydon at mozilla.com (Graydon Hoare) Date: Thu, 10 May 2012 11:22:20 -0700 Subject: [rust-dev] Const exprs (was: Re: RFC: Removing `as` (a WIP)) In-Reply-To: <4FABD73F.3020604@mozilla.com> References: <4FAB36B6.50200@alum.mit.edu> <4FABD73F.3020604@mozilla.com> Message-ID: <4FAC075C.6000204@mozilla.com> Further derailing the thread. Sorry, casts really got me thinking and I need to probe this issue a bit because a lot of near-term work depends on it: On 10/05/2012 7:57 AM, Graydon Hoare wrote: > So uh .. in the specific case of 'as' I'm not deeply worried what it > changes to, if anything. But I think we need to look at constant folding > first before making a move on it. Thought about this a bit more: - 'const fn' is, I think, overkill. Obliges the compiler to do a ton of additional work simulating runtime semantics, and gets the phases really mixed up, circular. - The scary case is integers sneaking into types, as in fixed-size arrays. I think the main case for this is actually C interop, where a syntax extension that reaches into clang, grabs C type-size information, and returns a literal is probably sufficient. I don't think it's necessary to generalize to "any constant expression can appear in a type". Which is good, because that would be a huge undertaking and very likely make the phases circular, to the point of total confusion. - Given that, sizeof/alignof/offsetof probably don't need to be const exprs. The other use-case for distinct integer-const exprs (as opposed to general const exprs) is in enum discriminant values, and I don't know exactly why they can't be pushed back to "some LLVM constant we'll get the actual value of later". I'm willing to find out, if someone can enlighten me! - Very little compiler-work is saved -- in fact I think it's more work -- to spill, emit a call to a visitor, and reload (and inline that), instead of just emitting integer-add or fp-mul. At least for types already well understood to the compiler, LLVM, and machine. - The primitive types already have privileged status in a few places, such as literal forms, presence in types (again, fixed vectors), and permitting multiple integer types for the RHS of << and [] and whatnot. - All this combines to make me think: - Integer, fp, raw pointer, region pointer, literal, addr-of, concatenation and indexing is an ok set of constant exprs. Evaluation happens in target mode, operands must themselves be constants, and we can do it in LLVM. It's just a line distinguishing "what we can define a 'const' as and have in read-only memory". - All such constant expressions (including literals, raw and region-pointer ops, fp and integer ops), have fixed compiler-implemented meaning. Anything more complex you want to do _at compile time_ you have to shell out to a syntax extension for. Maybe we provide a variable-precision fp calculator extension for fancy math constants. - All _nontrivial_ operations are routed through visitors, where "trivial" means that the operation _could_ have been a constant expr if only it had been applied to constant operands. IOW anything that boils down to 0 or 1 LLVM operation, memcmp or memmove is trivial. Anything touching the heap or a dynamic-sized value is nontrivial. I understand this is a somewhat ragged line cut through the space of constant-ness, but I think it can be defended in a principled way: the compiler does what compilers are good at -- primitives of various sorts, read-only memory slabs and addresses -- and the libraries do the rest. This does mean that sizeof, alignof and offsetof, applied to rust types, remain "runtime" values, even though the compiler implements them as intrinsics and they wind up as emitted / inlined constants. Again, I _think_ the main reason we'd want to have these functions evaluated much earlier -- i.e. "before types" -- has to do with C interop, and can be handled by a syntax extension that returns literals calculated by clang, but I could be wrong. Input welcome. (I'll paste this into the relevant bug, #2317, momentarily) -Graydon From graydon at mozilla.com Thu May 10 11:31:24 2012 From: graydon at mozilla.com (Graydon Hoare) Date: Thu, 10 May 2012 11:31:24 -0700 Subject: [rust-dev] RFC: Removing `as` (a WIP) In-Reply-To: <4FABF74B.9040605@alum.mit.edu> References: <4FAB36B6.50200@alum.mit.edu> <4FABD73F.3020604@mozilla.com> <4FABF74B.9040605@alum.mit.edu> Message-ID: <4FAC097C.8020601@mozilla.com> On 10/05/2012 10:13 AM, Niko Matsakis wrote: > That's a good point (as in const expressions). > > What do you think about the iface casting form? Fine with it. For runtime casts I'm fine with any such syntax. 13.uint() or uint(13) or ptr::cast(foo) or anything like that. Also worth noting though, concerning casts: we do a fair number of raw-pointer casts in unsafe code, "foo as *c_void" and such. Possibly solvable with a slightly more terse function like reinterpret_cast (eg. ptr::cast), but again, it breaks const exprs that might otherwise work via 'as'. Not sure how big a deal that is if we've given up on having such values evaluated "really early" (pre-types). See previous message on consts I just sent. -Graydon From niko at alum.mit.edu Thu May 10 11:32:46 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Thu, 10 May 2012 11:32:46 -0700 Subject: [rust-dev] Const exprs In-Reply-To: <4FAC075C.6000204@mozilla.com> References: <4FAB36B6.50200@alum.mit.edu> <4FABD73F.3020604@mozilla.com> <4FAC075C.6000204@mozilla.com> Message-ID: <4FAC09CE.9060701@alum.mit.edu> On 5/10/12 11:22 AM, Graydon Hoare wrote: > Further derailing the thread. Sorry, casts really got me thinking and > I need to probe this issue a bit because a lot of near-term work > depends on it: This all sounds pretty reasonable to me. I think we should draw the line somewhere simple for now and only extend it if/when it proves necessary. Niko From graydon at mozilla.com Thu May 10 11:39:55 2012 From: graydon at mozilla.com (Graydon Hoare) Date: Thu, 10 May 2012 11:39:55 -0700 Subject: [rust-dev] RFC: Removing `as` (a WIP) In-Reply-To: <4FAC0169.1030109@gmail.com> References: <4FAB36B6.50200@alum.mit.edu> <4FABD73F.3020604@mozilla.com> <4FAC0169.1030109@gmail.com> Message-ID: <4FAC0B7B.1050502@mozilla.com> On 10/05/2012 10:56 AM, Gareth Smith wrote: > Just a thought - how about `sys::enum_value(x: T) -> uint` > where c_enum is some new kind given only to c-style enums? Possible. Feels like overkill just to support the one function, could just as easily have the compiler emit an error for the one case in question. -Graydon From james at mansionfamily.plus.com Sat May 12 04:24:48 2012 From: james at mansionfamily.plus.com (james) Date: Sat, 12 May 2012 12:24:48 +0100 Subject: [rust-dev] Question about task monitoring and ports Message-ID: <4FAE4880.80103@mansionfamily.plus.com> I am interested in Rust as a possible alternative to Erlang. I'm not a committed Erlang dev at all - just starting (as a long-time C++ dev), so Rust is attractive as a slightly less bonkers syntax. And it should be (a lot) faster. Erlang seems to me to have two big advantages: - localised GC by task - task monitoring Its not clear to me that the former is (or could be, given the memory model for shared data) on the cards, but I'm interested in the latter. The docs suggest that a task's state can be polled - but that really sucks IMO. I couldn't find a way to subscribe to a 'death event' for a task - is there one? It seems to me that it would make life much easier in terms of robustness. Also, it would be handy to bind channels to tasks so that they automatically become invalidated if there is no longer a consumer. Also for a port, it is not clear what happens if a task is blocked in comm::recv and the port should be closed or otherwise invalidated (I would hope to return in an error state) or if some timeout occurs and I want to wake up - I know it is possible to peek but that suggests a polling structure again if one doesn't want to get stuck with tasks in recv when you want to tear down some or all of the app. Thanks James From pwalton at mozilla.com Sat May 12 09:49:51 2012 From: pwalton at mozilla.com (Patrick Walton) Date: Sat, 12 May 2012 09:49:51 -0700 Subject: [rust-dev] Question about task monitoring and ports In-Reply-To: <4FAE4880.80103@mansionfamily.plus.com> References: <4FAE4880.80103@mansionfamily.plus.com> Message-ID: <4FAE94AF.8000100@mozilla.com> On 05/12/2012 04:24 AM, james wrote: > I am interested in Rust as a possible alternative to Erlang. I'm not a > committed Erlang dev at all - just starting (as a long-time C++ dev), so > Rust is attractive as a slightly less bonkers syntax. And it should be > (a lot) faster. > > Erlang seems to me to have two big advantages: > - localised GC by task Rust has this. Patrick From niko at alum.mit.edu Sat May 12 10:03:26 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Sat, 12 May 2012 10:03:26 -0700 Subject: [rust-dev] Question about task monitoring and ports In-Reply-To: <4FAE4880.80103@mansionfamily.plus.com> References: <4FAE4880.80103@mansionfamily.plus.com> Message-ID: <4FAE97DE.6010503@alum.mit.edu> On 5/12/12 4:24 AM, james wrote: > Its not clear to me that the former is (or could be, given the memory > model for shared data) on the cards, but I'm interested in the latter. Rust does indeed have localized GC per task. It's one of the major design goals. > The docs suggest that a task's state can be polled - but that really > sucks IMO. I couldn't find a way to subscribe to a 'death event' for a > task - is there one? There is. When you spawn the task, you must set the notify channel. The task will then fire a message to this channel when it finishes. You can set the notify channel by using `set_opts()` on the task builder. > Also, it would be handy to bind channels to tasks so that they > automatically become invalidated if there is no longer a consumer. This is precisely what happens. You can send to a channel tied to a dead port. The message just gets dropped on the floor. > Also for a port, it is not clear what happens if a task is blocked in > comm::recv and the port should be closed or otherwise invalidated (I > would hope to return in an error state) or if some timeout occurs and > I want to wake up - I know it is possible to peek but that suggests a > polling structure again if one doesn't want to get stuck with tasks in > recv when you want to tear down some or all of the app. I'm not quite sure what you mean here, actually. I know we are working on timers and so forth but I'm not sure why the task should be closed while it is blocked on the port. One scenario I can think of would be Erlang like kill-groups (which we don't currently have), but usually in our system at least tasks kill themselves, so if they are blocked they don't need to die. In general, our task model is pretty close to Erlang, but task death is one of those places where we differ. I am not sure if that's a good thing or not. The Erlang model strikes me as a bit odd?it seems like most apps use a directed, hierarchical graph built on a set abstraction, and that seems backward?but I am nowhere near experienced enough with Erlang to say that with any confidence. Niko From james at mansionfamily.plus.com Sat May 12 12:52:09 2012 From: james at mansionfamily.plus.com (james) Date: Sat, 12 May 2012 20:52:09 +0100 Subject: [rust-dev] Question about task monitoring and ports In-Reply-To: <4FAE97DE.6010503@alum.mit.edu> References: <4FAE4880.80103@mansionfamily.plus.com> <4FAE97DE.6010503@alum.mit.edu> Message-ID: <4FAEBF69.9010305@mansionfamily.plus.com> > On 5/12/12 4:24 AM, james wrote: >> Its not clear to me that the former is (or could be, given the memory model for shared data) on the cards, but I'm interested in the latter. > > Rust does indeed have localized GC per task. It's one of the major design goals. OK, that's nice. Is this in the docs? > >> The docs suggest that a task's state can be polled - but that really sucks IMO. I couldn't find a way to subscribe to a 'death event' for a task - is there one? > > There is. When you spawn the task, you must set the notify channel. The task will then fire a message to this channel when it finishes. You can set the notify channel by using `set_opts()` on the task builder. OK but in Erlang its also possible to start monitoring after the fact. I think that is useful when doing internal pub/sub or callback architectures - you can tell whether your clients are dead. It may just mean that I have to have a central service that tracks everything else and broadcasts, but it seems to me that this is handy in the runtime. > >> Also, it would be handy to bind channels to tasks so that they automatically become invalidated if there is no longer a consumer. > > This is precisely what happens. You can send to a channel tied to a dead port. The message just gets dropped on the floor. Well, that's not what I meant, in that I might want to get an error indication, for the same reason as above. > >> Also for a port, it is not clear what happens if a task is blocked in comm::recv and the port should be closed or otherwise invalidated (I would hope to return in an error state) or if some timeout occurs and I want to wake up - I know it is possible to peek but that suggests a polling structure again if one doesn't want to get stuck with tasks in recv when you want to tear down some or all of the app. > > I'm not quite sure what you mean here, actually. I know we are working on timers and so forth but I'm not sure why the task should be closed while it is blocked on the port. One scenario I can think of would be Erlang like kill-groups (which we don't currently have), but usually in our system at least tasks kill themselves, so if they are blocked they don't need to die. If I have tasks waiting for messages from a dispatcher and the dispatcher dies (and perhaps if the whole app is closing down) then its handy to have at very least some whay to indicate that the app is shutting down globally, and for everything to check this periodically if it is otherwise idle. Erlang's wait with timeout (and indeed the timeout waits for locks and condition variables normal in shared data synchronisation models) make this handy. I mistrust 'wait for ever' models - its easy to lose track of something and have trouble recovering. Yhanks James From banderson at mozilla.com Mon May 14 14:33:52 2012 From: banderson at mozilla.com (Brian Anderson) Date: Mon, 14 May 2012 14:33:52 -0700 Subject: [rust-dev] Question about task monitoring and ports In-Reply-To: <4FAEBF69.9010305@mansionfamily.plus.com> References: <4FAE4880.80103@mansionfamily.plus.com> <4FAE97DE.6010503@alum.mit.edu> <4FAEBF69.9010305@mansionfamily.plus.com> Message-ID: <4FB17A40.8030007@mozilla.com> On 05/12/2012 12:52 PM, james wrote: >> On 5/12/12 4:24 AM, james wrote: >>> Its not clear to me that the former is (or could be, given the memory >>> model for shared data) on the cards, but I'm interested in the latter. >> >> Rust does indeed have localized GC per task. It's one of the major >> design goals. > > OK, that's nice. Is this in the docs? > >> >>> The docs suggest that a task's state can be polled - but that really >>> sucks IMO. I couldn't find a way to subscribe to a 'death event' for >>> a task - is there one? >> >> There is. When you spawn the task, you must set the notify channel. >> The task will then fire a message to this channel when it finishes. >> You can set the notify channel by using `set_opts()` on the task builder. > > OK but in Erlang its also possible to start monitoring after the fact. I > think that is useful when doing internal pub/sub or callback > architectures - you can tell whether your clients are dead. > > It may just mean that I have to have a central service that tracks > everything else and broadcasts, but it seems to me that this is handy in > the runtime. > >> >>> Also, it would be handy to bind channels to tasks so that they >>> automatically become invalidated if there is no longer a consumer. >> >> This is precisely what happens. You can send to a channel tied to a >> dead port. The message just gets dropped on the floor. > > Well, that's not what I meant, in that I might want to get an error > indication, for the same reason as above. I would be interested in the use cases for this. It would be easy expose whether a port is still alive, but that doesn't help you to know whether anybody will ever receive data sent to that port (maybe it dies immediately after you check). >> >>> Also for a port, it is not clear what happens if a task is blocked in >>> comm::recv and the port should be closed or otherwise invalidated (I >>> would hope to return in an error state) or if some timeout occurs and >>> I want to wake up - I know it is possible to peek but that suggests a >>> polling structure again if one doesn't want to get stuck with tasks >>> in recv when you want to tear down some or all of the app. >> >> I'm not quite sure what you mean here, actually. I know we are working >> on timers and so forth but I'm not sure why the task should be closed >> while it is blocked on the port. One scenario I can think of would be >> Erlang like kill-groups (which we don't currently have), but usually >> in our system at least tasks kill themselves, so if they are blocked >> they don't need to die. > > If I have tasks waiting for messages from a dispatcher and the > dispatcher dies (and perhaps if the whole app is closing down) then its > handy to have at very least some whay to indicate that the app is > shutting down globally, and for everything to check this periodically if > it is otherwise idle. Erlang's wait with timeout (and indeed the timeout > waits for locks and condition variables normal in shared data > synchronisation models) make this handy. I mistrust 'wait for ever' > models - its easy to lose track of something and have trouble recovering. Wait with timeout is currently called `std::timer::recv_timeout`. The current behavior around runtime shutdown is that, when the runtime decides it's time for the app to exit, it will kill all running tasks, so every task blocking on a port will wake up and unwind on its own. The task API is still evolving, especially around task killing and linked failure, so contributions are very welcome. From james at mansionfamily.plus.com Mon May 14 15:23:44 2012 From: james at mansionfamily.plus.com (james) Date: Mon, 14 May 2012 23:23:44 +0100 Subject: [rust-dev] Question about task monitoring and ports In-Reply-To: <4FB17A40.8030007@mozilla.com> References: <4FAE4880.80103@mansionfamily.plus.com> <4FAE97DE.6010503@alum.mit.edu> <4FAEBF69.9010305@mansionfamily.plus.com> <4FB17A40.8030007@mozilla.com> Message-ID: <4FB185F0.2090106@mansionfamily.plus.com> >> Well, that's not what I meant, in that I might want to get an error >> indication, for the same reason as above. > > I would be interested in the use cases for this. It would be easy expose whether a port is still alive, but that doesn't help you to know whether anybody will ever receive data sent to that port (maybe it dies immediately after you check). Well, the interesting case is not that something tests alive but dies before I test again, but to indicate that something is definitely dead, and whatever resources I have related to generating content are not needed anymore. > > Wait with timeout is currently called `std::timer::recv_timeout`. Given that the blocking case is effectively 'wait with infinite timeout' I think its very confusing to seperate the two. > > The current behavior around runtime shutdown is that, when the runtime decides it's time for the app to exit, it will kill all running tasks, so every task blocking on a port will wake up and unwind on its own. I'm interested however in my applications idea of when the application should exit - rather than some system view of it. Perhaps I have a C++ dev's idea of this, but its handy to have some way to know that for example 'all' threads will 'soon' return from blocking waits and will check and then start to unwind, so that they can be joined in some sort of ordered way. It does seem uncontrolled for everything to be running and then closing in a system chosen and arbitrarily ordered way is potentially scary, especially if you have C libraries that you want to close down in an ordered way if at all possible. James From niko at alum.mit.edu Fri May 18 11:46:12 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Fri, 18 May 2012 11:46:12 -0700 Subject: [rust-dev] internal command line options Message-ID: <4FB698F4.3010602@alum.mit.edu> I changed rustc so that options that are intended to aid in debugging rustc are now prefixed with -Z and do not appear in the default --help screen. So, for example, if you want to time the rustc passes, you do: rustc -Z time-passes foo.rs If you want to find out all of the internal options, do: rustc -Z help Niko From bklooste at gmail.com Sun May 20 05:23:01 2012 From: bklooste at gmail.com (Bennie Kloosteman) Date: Sun, 20 May 2012 20:23:01 +0800 Subject: [rust-dev] HasField Message-ID: New member to the list im no genius but am looking for a replacement for c for system programming. 1) Great work on UTF8 strings and USC4 chars ( I do some work with chinese chars and USC-2 just doesn't work here) . BTW why null terminated strings if you have arrays . Any c interop will require conversion to Unicode chars anyway fr a generic lib ( and for most IO the perf cost of a copy is pretty irrelevant) , Speaking of which its very useful in strings keeping the char count as well as the byte length mainly so you can go if ( str->length == str->count) to avoid any Unicode parsing when you convert to ascii. ( you could use a flag or the high bits of a int64 ) - or use a subtype stating its ASCII. 2) Does Rust have anything like bitc HasField ,? BitC introduced has field constraint and this was very useful , the field could be a function or property but you can then put on a method that the type HasField x and like an interface it hides the implementation , you could use any type with met the constraint. . It also meant encapsulated types and the associated "this" pointer / vcall issues could possible be avoided since the method only specified what filed it used the rest would be private ( for that method) .To me this greatly reduced the need of having private fields/members which existing c programmers will just follow as they find it hard to pick up type classes initially. You may be able to use hashfield for a fascade to further effect to hide internals . Since you guys are now considering this and objects I would read the below comments very carefully. BitC also had a major issues working with type classes and objects namely that it was very confusing whether to use traits( type classes) or classes for polymorphism , there was a huge dichotomy which was confusing and classes with private members made this worse.. Obviously the main issue was cross unit compilation with multiple types but I believe you guys have an answer to that.. Here is what Shap wrote about has field in his summary.. "In BitC, we introduced a built-in type class *has-field*. If struct S has field *f* with type *T*, then has-field(S, f, T). This was a bit of a kludge, in that the language doesn't admit atoms in general, but that's minor and fixable. The interesting point is that it generalizes readily to member functions and to structure methods (which we *did* have). Which means that it can provide something very similar to a subclass relation at parameters. To the extent that this works, *has-field* subsumes inheritance. What *has-field* does *not* subsume is virtual member functions, though it can actually be made to serve there. The problem is that without some form of encapsulated This type, we end up with parametricity rather than encapsulation. In most cases this is okay, but in some cases we really need encapsulation. The single inheritance pattern, whatever its flaws, provides a nicely controlled form of encapsulation. So this isn't a critique of type classes as much as it is an observation that type classes and object classes aren't the same thing and aren't interchangeable. The catch is that you are now forced to contemplate the introduction of object classes and inheritance. Once you do that, you end up in a place where *almost* everything done by type classes and type class instances can now be done by classes and object instances. The only thing missing, really, is a way to get the deep constant and compile-time constant properties captured so that we can inline things aggressively enough to use this approach at ground operators. And *that* actually seems to me like something that could be done. In the end, I'm left wondering what purpose general type classes would still serve in such a system. The built-in classes continue to have value as a way to capture, trace, and manage constraints within the compiler implementation, but that's quite different from user-extensible classes." Ben From pwalton at mozilla.com Sun May 20 09:06:55 2012 From: pwalton at mozilla.com (Patrick Walton) Date: Sun, 20 May 2012 09:06:55 -0700 Subject: [rust-dev] HasField In-Reply-To: References: Message-ID: <4FB9169F.6050107@mozilla.com> On 05/20/2012 05:23 AM, Bennie Kloosteman wrote: > New member to the list im no genius but am looking for a replacement > for c for system programming. > > 1) Great work on UTF8 strings and USC4 chars ( I do some work with > chinese chars and USC-2 just doesn't work here) . BTW why null > terminated strings if you have arrays . Any c interop will require > conversion to Unicode chars anyway fr a generic lib ( and for most > IO the perf cost of a copy is pretty irrelevant) At the moment we aren't willing to risk that the cost of a copy for C interoperability is irrelevant. Might be worth measuring once we have more of Servo up and running though. (The operative constraint here is basically that it's fast enough to build a competitive browser with.) > , Speaking of which > its very useful in strings keeping the char count as well as the byte > length mainly so you can go if ( str->length == str->count) to avoid > any Unicode parsing when you convert to ascii. ( you could use a > flag or the high bits of a int64 ) - or use a subtype stating its > ASCII. Interesting idea. Patches welcome! > 2) Does Rust have anything like bitc HasField ,? > > BitC introduced has field constraint and this was very useful , the > field could be a function or property > but you can then put on a method that the type HasField x and like > an interface it hides the implementation , > you could use any type with met the constraint. . I don't see any reason why we couldn't have HasField, although none of us have needed it yet. > It also meant encapsulated types and the associated "this" pointer / > vcall issues could possible be avoided since the method only specified > what filed it used the rest would be private ( for that method) .To > me this greatly reduced the need of having private fields/members > which existing c programmers will just follow as they find it hard to > pick up type classes initially. You may be able to use hashfield for > a fascade to further effect to hide internals . Since you guys are > now considering this and objects I would read the below comments very > carefully. I have ideas as to how we could implement inheritance if we need it (and I think we might well need it, as I'm running into issues with not being able to exploit the prefix property for subclassing with Servo). It's not totally sketched out, so I don't have a concrete proposal. That notwithstanding, basically it involves unifying enums and classes so that (a) enum variants are actual types, subtypes of the enclosing enum to be exact; (b) enums are classes and can have fields and methods; (c) enum variants can access fields and methods of the enclosing enum; (d) enum variants can contain other sub-variants. Patrick From dteller at mozilla.com Mon May 21 02:31:36 2012 From: dteller at mozilla.com (David Rajchenbach-Teller) Date: Mon, 21 May 2012 11:31:36 +0200 Subject: [rust-dev] Back to errors, failures and exceptions Message-ID: <4FBA0B78.2080304@mozilla.com> Dear Rusties, As you may recall, a few months ago, we had a lengthy and interesting conversation about how to best represent errors and failures in Rust. The thread eventually ended when everybody had run out of ideas and arguments, before any conclusion could be reached. Since the end of that thread, I have given it a little more thought, and I now believe that: 1/ the original premise still stands ? we need guidelines on how to handle errors, failures and other exceptional circumstances if we want to avoid ending up in the same mess that Haskell, OCaml or C on Windows; 2/ Graydon was more right than me; 3/ we cannot work without a base of examples. So, I would like to use this thread to start gathering a few real-world examples that could serve as support for discussion on any error/failure/other mechanism/library/guidelines. I will start with my next message. Cheers, David -- David Rajchenbach-Teller, PhD Performance Team, Mozilla -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 487 bytes Desc: OpenPGP digital signature URL: From dteller at mozilla.com Mon May 21 04:10:44 2012 From: dteller at mozilla.com (David Rajchenbach-Teller) Date: Mon, 21 May 2012 13:10:44 +0200 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FBA0B78.2080304@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> Message-ID: <4FBA22B4.2070901@mozilla.com> I am currently implementing a low-level API for file handling, for the Mozilla Platform. This API is based on libc/winapi (which do not have exceptions) but itself throws exceptions in case of errors. This causes any number of "amusing"/error-prone situations, in which the implementation of the API combines functions that do not throw exceptions (because these errors are expected to be handled immediately) and functions that do throw exceptions (because we do not want to forget reporting errors), |errno|-based error checking, etc. For instance, let us consider the following (otherwise rather simple) function |move|, whose role is to rename/move a file or a directory, possibly across devices. Function move(from, to, options) 1. - If |options| specifies that we should not overwrite 2. - Attempt to |open| for reading |to| 3. - If the call succeeds, close file and bail out (destination exists). 4. - If the call fails due to insufficient rights for opening |to|, bail out (destination exists). 5. - If the call fails due to |to| being ill-formed or too long, bail out (client error). 6. - Call libc |rename(from, to)| 7. - If the call succeeds, we are done. 8. - If the call fails due to files not being on the same device, proceed. 9. - If the call fails for any other reason, bail out (propagate error as received). 10. - Call function |copy|. 11. - If the call fails, bail out (error as received). 12. - Call function |remove|. 13. - If the call fails, bail out (error as received). 14. - We have succeeded This simple function already illustrates the following cases: - Call to |open|: - catch and ignore most errors; - propagate some errors; - turn success into an error. - Call to |rename|: - catch and ignore one error; - propagate all other errors; - success is a success. - Calls to |copy| and |remove|: - propagate all errors Now, I have not encountered yet a scenario in which errors raised by |rename| are handled/ignored in a fine-grained way, but this will certainly happen. Cheers, David -- David Rajchenbach-Teller, PhD Performance Team, Mozilla -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 487 bytes Desc: OpenPGP digital signature URL: From niko at alum.mit.edu Mon May 21 12:45:21 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Mon, 21 May 2012 12:45:21 -0700 Subject: [rust-dev] agenda Message-ID: <4FBA9B51.3030808@alum.mit.edu> https://etherpad.mozilla.org/Meeting-weekly-2012-05-22 From bklooste at gmail.com Mon May 21 19:56:58 2012 From: bklooste at gmail.com (Bennie Kloosteman) Date: Tue, 22 May 2012 10:56:58 +0800 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FBA22B4.2070901@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> Message-ID: Are exceptions a good model for systems programming ? - legacy C programs cant call you without a wrapper which translates all possible exceptions - unwinding a stack is probably not a good idea in a kernel or when you transition into protected/user mode.( I know of very few kernels that use exceptions ). - Its not just legacy , Winrt uses C++ classes but returns error codes tor low level APIs. However its very nice for user programs . These days these different worlds works quite well , c libs which Is mainly used for system programming don't use them and C++ apps are more user programs and they do , C++ calls C , C rarely calls C++. Obviously if you write a kernel or shared library you cannot use exceptions if c programs call your code and there is a lot of c out there.... While not really an issue for the language ( just dont use exceptions) it means a standard lib that throws an exception would be a pain for such work and you would need a different standard lib , which is an issue . BTW could Rust use tasks as a substitute for exception scopes ? Tasks have error bubbling , hiding , "stack unwinding" , throw ( fail) and should have integrated logging . You could put a sugar syntax around it but it would still work when being called by c. Also with tasks you can cancel or do timeouts giving asynronous exceptions which are really needed ( eg in most systems cancel a long running task is very anoying very long pause). and which most trivial exception implementations don't do ..Not sure if this is the right way but there seems a lot of overlap and it would work with C and systems programming,. Ben From bklooste at gmail.com Mon May 21 22:19:25 2012 From: bklooste at gmail.com (Bennie Kloosteman) Date: Tue, 22 May 2012 13:19:25 +0800 Subject: [rust-dev] HasField In-Reply-To: References: <4FB9169F.6050107@mozilla.com> Message-ID: Forwarded to the list. Thanks for your reply Patrick. BTW love the single threaded Task based approach , I was working on an runtime which I have had to stop ( i was waiting on bitc) which was based on lots of single threaded processes resizable stacks with point to point pipes and was deeply asynchronous ie completely non blocking ( well almost but the iPC was non blocking , blocking would occur when waiting on a resource like disk or memory) . The runtime would control the scheduling of tasks ( eg compression , encryption) and can add , reduce them on a machine wide basis ( and recover from errors) . Since all requests were asynchronous system wide hotspots could get additional resources. Anyway you can see my interest in Rust though your tasks are more fine grained. On Mon, May 21, 2012 at 12:06 AM, Patrick Walton wrote: > On 05/20/2012 05:23 AM, Bennie Kloosteman wrote: >> >> New member to the list ?im no genius but am looking for a replacement >> for c for system programming. >> >> 1) ?Great work on UTF8 strings and USC4 chars ( I do some work with >> chinese chars and USC-2 just doesn't work here) . BTW why null >> terminated strings ?if you have arrays . Any c interop will require >> conversion to Unicode chars anyway fr a generic lib ?( and for ?most >> IO the perf cost of a copy is pretty irrelevant) > > > At the moment we aren't willing to risk that the cost of a copy for C > interoperability is irrelevant. Might be worth measuring once we have more > of Servo up and running though. (The operative constraint here is basically > that it's fast enough to build a competitive browser with.) I don't think its good design to cover the specific "browser" case or when you have doubts on the impact to then design it for performance before you have measured it to be an issue ..If your writing a browser than 90% of desktop platforms are windows where you need to convert to USC-2 so no impact. ? The same design decision would say my most common bench mark will be windows so I should have utf16 strings. ?( That is not my opinion I like UTF8) . The bigger issue , in most cases you don't know the UTF8 string is ascii ?, so you need to parse it each time ?before passing to a c lib and fault if ?you have a UTF8 string and are calling an ascii API. In most cases the code will look like this right if ( receiveLib.StringType == ASCII) { if ( str.IsAscii() ? ? ? ? ? callAscii (str) else ? ? ? fault } if ( receiveLib.StringType == USC2()) ?{ ? ? ?let unistr = ?str.GetUCS2() ? ? ?callunicode(unistr) } if ( receiveLib.StringType == UTF8()) { ? ? callutf8(str) } the above would be quite annoying for multi platform apps in lots of locations since you already have c string and str ( utf8) ?is it worth burying them under a qstring like construct in the standard lib ? ?In which case it would be ?let qstr = qstr.GetString(receiveLib.StringType ); callLib(qstr); > > >> , ? Speaking of which >> its very useful in utf8 [edit] strings keeping the char count as well as the byte >> length mainly so you can go ?if ( str->length == str->count) ?to avoid >> any Unicode parsing when you convert to ascii. ? ( you could use a >> flag or the high bits of a int64 ) ?- or use a subtype stating its >> ASCII. > > > Interesting idea. Patches welcome! UTF8 strings in a runtime is pretty rare ( C is ascii which supports UTF8 but only for non character based functions) and I think this is needed for interop. eg parsing looking for null ?or 10 UTF8 escapes can be costly. So you need to know is it ASCII or Unicode to call the appropriate lib call when you work with chars. ( Converting everything to USC-4 when you do char based work is too expensive) . BTW if Rust strings are null terminated why is there a from/to cstr is it because they were vectors and when you have arrays it disappears ? A patch is a bit beyond me yet I just started looking at rust ,and would want to do so some quick and dirt apps first, ?if I do commit it will be more seriously > > >> 2) Does Rust have anything like bitc ?HasField ?,? >> >> BitC introduced has field constraint and this was very useful , ?the >> field could be a function or property >> but you can then put on a method that the type HasField ?x and ?like >> an interface it hides the implementation ?, >> you could use any type with met the constraint. . > > > I don't see any reason why we couldn't have HasField, although none of us > have needed it yet. If your talking about encapsulation than you do need something like it. I would say for larger projects and libs ?- ?You don't need private variables with hasfield and it avoids confusion with the increasing C++ class model (bitc ?hit a stumble there) ?- For larger project and public libs ?you can export hasfield to hide the internal implementation details. ?This would be better IMHO ( due to the above) than introducing private variables. - Note has field can work on fields which includes function pointers , length on an array (but without exposing the array data...) > > >> It also meant encapsulated types and the associated "this" pointer / >> vcall issues could possible be avoided since the method only specified >> what filed it used the rest would be private ?( for that method) .To >> me this greatly reduced the need of having private fields/members >> which existing c programmers will just follow as they find it hard to >> pick up type classes initially. ? You may be able to use hashfield for >> a fascade ?to further effect to hide internals . Since you guys are >> now considering this and objects ?I would read the below comments very >> carefully. > > > I have ideas as to how we could implement inheritance if we need it (and I > think we might well need it, as I'm running into issues with not being able > to exploit the prefix property for subclassing with Servo). It's not totally > sketched out, so I don't have a concrete proposal. That notwithstanding, > basically it involves unifying enums and classes so that (a) enum variants > are actual types, subtypes of the enclosing enum to be exact; (b) enums are > classes and can have fields and methods; (c) enum variants can access fields > and methods of the enclosing enum; (d) enum variants can contain other > sub-variants. all this is pretty strange to me no idea what servo us , ?still reading on enums ?, the enum name is confusing from a C /C# background ?as it is not an enumeration of values but more a union of types ?( or for d) a struct./ value type ) or even a class pulling functions and data together. ? Doesn't this association work better on a separate name ( class , struct , trait ) Be very careful here trying to be all things , ? that's the path Bitc went down and eventually you have a C#/Java class system with single inheritance and you have generics and then you ask , why do I need Type classes. You will get Typeclass style libs and Java style libs but they wont ?cooperate elegantly since one lib function calls are on type classes and the other on structs / classes/ In BITC they wanted inheritance to port the compiler from C++ , however most system programming is in C and not C++ . If you can solve the module compile issue ( eg DLL /so 's ) for multiple type classes , than I think avoided classes , encapsulation , virtual calls ?and inheritance . To me the integration of these type systems ( type classes and C#/Java like objects ) is research and has not been done before and a few case statements or function pointers on structs for some manual polymorphism are not a big issue as the places that needs them with type classes is low ( except for porting ) Ben From dteller at mozilla.com Wed May 23 05:47:36 2012 From: dteller at mozilla.com (David Rajchenbach-Teller) Date: Wed, 23 May 2012 14:47:36 +0200 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> Message-ID: <4FBCDC68.9020205@mozilla.com> Actually, one of the conclusions of our previous discussion is that Java/C++/ML-style exceptions are probably not what we want for Rust. I seem to remember that we also concluded that using failures as exceptions was probably not the right course. Hence this new thread :) Let me put together what I believe are a few desirable qualities of an "issue management system". For the moment, let's not wonder whether that system is a language feature, a library or a coding guideline. * The system _must_ not prevent developers from calling C code from Rust. * The system _must_ not prevent developers from passing a pointer to a Rust function to C code that will call back to it. * The system _must_ not prevent, some day, developers from calling Rust from JavaScript. * The system _must_ not prevent, some day, developers from calling JavaScript from Rust. * Issues _must_ not be restricted to integers (or to one single type). * The default behavior in case of untreated issue _should_ be to gracefully kill the task or the application. * Whenever an untreated issue kills a task/application, it _should_ produces a report usable by the developer for fixing the issue. * It _should_ be possible to deactivate that killing behavior. There _may_ be limitations. * It _should_ be possible to deactivate that killing behavior conditionally (i.e. only for some errors). * The system _should_ eventually have a low runtime cost ? in particular, the case in which no killing happens should be very fast. Do we agree on this base? Cheers, David On 5/22/12 4:56 AM, Bennie Kloosteman wrote: > Are exceptions a good model for systems programming ? > > - legacy C programs cant call you without a wrapper which translates > all possible exceptions > - unwinding a stack is probably not a good idea in a kernel or when > you transition into protected/user mode.( I know of very few kernels > that use exceptions ). > - Its not just legacy , Winrt uses C++ classes but returns error codes > tor low level APIs. > > However its very nice for user programs . These days these different > worlds works quite well , c libs which Is mainly used for system > programming don't use them and C++ apps are more user programs and > they do , C++ calls C , C rarely calls C++. Obviously if you write a > kernel or shared library you cannot use exceptions if c programs > call your code and there is a lot of c out there.... While not really > an issue for the language ( just dont use exceptions) it means a > standard lib that throws an exception would be a pain for such work > and you would need a different standard lib , which is an issue . > > BTW could Rust use tasks as a substitute for exception scopes ? Tasks > have error bubbling , hiding , "stack unwinding" , throw ( fail) and > should have integrated logging . You could put a sugar syntax around > it but it would still work when being called by c. Also with tasks > you can cancel or do timeouts giving asynronous exceptions which are > really needed ( eg in most systems cancel a long running task is very > anoying very long pause). and which most trivial exception > implementations don't do ..Not sure if this is the right way but there > seems a lot of overlap and it would work with C and systems > programming,. > > Ben > _______________________________________________ > Rust-dev mailing list > Rust-dev at mozilla.org > https://mail.mozilla.org/listinfo/rust-dev -- David Rajchenbach-Teller, PhD Performance Team, Mozilla -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 487 bytes Desc: OpenPGP digital signature URL: From matthieu.monrocq at gmail.com Wed May 23 11:28:26 2012 From: matthieu.monrocq at gmail.com (Matthieu Monrocq) Date: Wed, 23 May 2012 20:28:26 +0200 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FBCDC68.9020205@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> Message-ID: On Wed, May 23, 2012 at 2:47 PM, David Rajchenbach-Teller < dteller at mozilla.com> wrote: > Actually, one of the conclusions of our previous discussion is that > Java/C++/ML-style exceptions are probably not what we want for Rust. I > seem to remember that we also concluded that using failures as > exceptions was probably not the right course. > > Hence this new thread :) > > Let me put together what I believe are a few desirable qualities of an > "issue management system". For the moment, let's not wonder whether that > system is a language feature, a library or a coding guideline. > > As a whole, this looks very good to me, I just have one quick question: > * The system _must_ not prevent developers from calling C code from Rust. > * The system _must_ not prevent developers from passing a pointer to a > Rust function to C code that will call back to it. > * The system _must_ not prevent, some day, developers from calling Rust > from JavaScript. > * The system _must_ not prevent, some day, developers from calling > JavaScript from Rust. > * Issues _must_ not be restricted to integers (or to one single type). > Could you explain what you mean by this ? I suppose this is a direct jab at the horror that is errno and more in the direction of being able to "throw" anything (possibly at the condition it implements a given interface) ? > * The default behavior in case of untreated issue _should_ be to > gracefully kill the task or the application. > * Whenever an untreated issue kills a task/application, it _should_ > produces a report usable by the developer for fixing the issue. > * It _should_ be possible to deactivate that killing behavior. There > _may_ be limitations. > * It _should_ be possible to deactivate that killing behavior > conditionally (i.e. only for some errors). > * The system _should_ eventually have a low runtime cost ? in > particular, the case in which no killing happens should be very fast. > > > Do we agree on this base? > > Cheers, > David > > On 5/22/12 4:56 AM, Bennie Kloosteman wrote: > > Are exceptions a good model for systems programming ? > > > > - legacy C programs cant call you without a wrapper which translates > > all possible exceptions > > - unwinding a stack is probably not a good idea in a kernel or when > > you transition into protected/user mode.( I know of very few kernels > > that use exceptions ). > > - Its not just legacy , Winrt uses C++ classes but returns error codes > > tor low level APIs. > > > > However its very nice for user programs . These days these different > > worlds works quite well , c libs which Is mainly used for system > > programming don't use them and C++ apps are more user programs and > > they do , C++ calls C , C rarely calls C++. Obviously if you write a > > kernel or shared library you cannot use exceptions if c programs > > call your code and there is a lot of c out there.... While not really > > an issue for the language ( just dont use exceptions) it means a > > standard lib that throws an exception would be a pain for such work > > and you would need a different standard lib , which is an issue . > > > > BTW could Rust use tasks as a substitute for exception scopes ? Tasks > > have error bubbling , hiding , "stack unwinding" , throw ( fail) and > > should have integrated logging . You could put a sugar syntax around > > it but it would still work when being called by c. Also with tasks > > you can cancel or do timeouts giving asynronous exceptions which are > > really needed ( eg in most systems cancel a long running task is very > > anoying very long pause). and which most trivial exception > > implementations don't do ..Not sure if this is the right way but there > > seems a lot of overlap and it would work with C and systems > > programming,. > > > > Ben > > _______________________________________________ > > Rust-dev mailing list > > Rust-dev at mozilla.org > > https://mail.mozilla.org/listinfo/rust-dev > > > -- > David Rajchenbach-Teller, PhD > Performance Team, Mozilla > > > _______________________________________________ > Rust-dev mailing list > Rust-dev at mozilla.org > https://mail.mozilla.org/listinfo/rust-dev > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From banderson at mozilla.com Wed May 23 11:37:52 2012 From: banderson at mozilla.com (Brian Anderson) Date: Wed, 23 May 2012 11:37:52 -0700 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FBCDC68.9020205@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> Message-ID: <4FBD2E80.4000808@mozilla.com> On 05/23/2012 05:47 AM, David Rajchenbach-Teller wrote: > Actually, one of the conclusions of our previous discussion is that > Java/C++/ML-style exceptions are probably not what we want for Rust. I > seem to remember that we also concluded that using failures as > exceptions was probably not the right course. > > Hence this new thread :) > > Let me put together what I believe are a few desirable qualities of an > "issue management system". For the moment, let's not wonder whether that > system is a language feature, a library or a coding guideline. > > * The system _must_ not prevent developers from calling C code from Rust. > * The system _must_ not prevent developers from passing a pointer to a > Rust function to C code that will call back to it. > * The system _must_ not prevent, some day, developers from calling Rust > from JavaScript. > * The system _must_ not prevent, some day, developers from calling > JavaScript from Rust. > * Issues _must_ not be restricted to integers (or to one single type). > * The default behavior in case of untreated issue _should_ be to > gracefully kill the task or the application. > * Whenever an untreated issue kills a task/application, it _should_ > produces a report usable by the developer for fixing the issue. > * It _should_ be possible to deactivate that killing behavior. There > _may_ be limitations. > * It _should_ be possible to deactivate that killing behavior > conditionally (i.e. only for some errors). > * The system _should_ eventually have a low runtime cost ? in > particular, the case in which no killing happens should be very fast. > > > Do we agree on this base? These points sound sensible to me. -Brian From dteller at mozilla.com Thu May 24 05:10:46 2012 From: dteller at mozilla.com (David Rajchenbach-Teller) Date: Thu, 24 May 2012 14:10:46 +0200 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> Message-ID: <4FBE2546.6010704@mozilla.com> On 5/23/12 8:28 PM, Matthieu Monrocq wrote: > As a whole, this looks very good to me, I just have one quick question: [...] > * Issues _must_ not be restricted to integers (or to one single type). > > > Could you explain what you mean by this ? > > I suppose this is a direct jab at the horror that is errno and more in > the direction of being able to "throw" anything (possibly at the > condition it implements a given interface) ? Exactly. Cheers, David -- David Rajchenbach-Teller, PhD Performance Team, Mozilla -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 487 bytes Desc: OpenPGP digital signature URL: From dteller at mozilla.com Thu May 24 06:16:35 2012 From: dteller at mozilla.com (David Rajchenbach-Teller) Date: Thu, 24 May 2012 15:16:35 +0200 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> Message-ID: <4FBE34B3.9060102@mozilla.com> On 5/24/12 12:40 PM, Bennie Kloosteman wrote: >> * The system _must_ not prevent developers from calling C code from Rust. >> * The system _must_ not prevent developers from passing a pointer to a >> Rust function to C code that will call back to it. >> * The system _must_ not prevent, some day, developers from calling Rust >> from JavaScript. >> * The system _must_ not prevent, some day, developers from calling >> JavaScript from Rust. > > These should be unsafe - I believe this is the case with rust. I am not sure if you mean this in relation with all four points or with the last one. So, indeed, I see no reason to change the fact that calls to C are considered unsafe. For calls to JavaScript, I have not given it much thought. I suppose that they could be either unsafe or somehow managed. >> * Issues _must_ not be restricted to integers (or to one single type). > > Yes but returning error codes and then looking up further issue > detail separately is quite useful since your passing around little > information it gives the highest chance in a partially unsafe > environment of working out what happened . eg if the stack is > corrupted or you have major issues its useful that the error is memory > logged ( eg in ring 0 in a rust based kernel ) and then retrieved . > A simple integer return value has a much better chance.. Interesting point. Are we talking about implementing Servo with a ring-like mechanism? If so, I suspect that we may simply not want this kind of issues to cross ring boundaries. Otherwise, if we let high-level issues cross boundaries, we could be sending closures, for instance, which sounds like a bad idea. For the moment, I believe that we should assume that the issue mechanism will not be used for crossing ring boundaries. > This could be a 64 bit value with the low 32 bits the error and the > upper 32 bit some sort of hash code. > > A more detailed but not guaranteed representation should be a detailed > structure. with time stamps etc I would even say this detail should > be created asynchronously via a new task while the actual error is > returned immediately . > > errno is a pain , but not because it returns an int but because all > the information you can retrieve on it is a error string. > > Actually you could log all the detail and just reference an index to > the log , Though this would require recent log entries to be > recallable and have more detail in the log fields eg error category > and level ( which are useful in a log anyway) All these are interesting points, although perhaps they arrive a little too early in the thread :) >> * The default behavior in case of untreated issue _should_ be to >> gracefully kill the task or the application. > > If its integrated with tracing and logging the default behaviour > depends on the error type > > eg Trace , Log, Warning or Error continue , Critical = halt. A tracer/logger with this kind of semantics could certainly be built on top of the issue mechanism. I tend to believe that that they should be integrated from the start, though. >> * Whenever an untreated issue kills a task/application, it _should_ >> produces a report usable by the developer for fixing the issue. >> * It _should_ be possible to deactivate that killing behavior. There >> _may_ be limitations. >> * It _should_ be possible to deactivate that killing behavior >> conditionally (i.e. only for some errors). > > also a task can monitor child tasks for errors ( I don't think Rust > has events but something like that) and choose on error to abort or > continue depending on the error condition. . Sounds interesting. I suspect (but I am not sure) that this is something that could/should be built on top of a more elementary issue mechanism. Could you provide a scenario that we could use as a working base? >> * The system _should_ eventually have a low runtime cost ? in >> particular, the case in which no killing happens should be very fast. > > I don't think the performance of an error system is important at all , > its more important to have stack and meta data to resolve the issue > especially in debug builds ( and meta data is very slow) . This leads > to people using errors to pass/check state which is a bad habit Could you elaborate on that last sentence? I am starting to believe that collecting [meta]data might be something that we want to configure on a usage basis. For instance, in function |move|, introduced a few e-mails ago (I just reposted it at https://gist.github.com/2781413 for convenience), we may be interested in the metadata of calls to lines 10 and 12, but gathering metadata for the calls in lines 2 and 6 is generally a waste of resources. If we think of this as exceptions, it could be that we want several the ability to specify if/when we want to gather metadata at the level of an equivalent of |try|. I believe that this fits nicely with what Graydon had in mind, by the way. > I would add > * Tasks are the error scope / unit > > * should we have an exception like syntax that automatically creates a > sub task for the embedded statements ( and hence means those > statements cannot result in failure of the parent tasks) . Since > errors are scoped to the task , the easy way is a new task . This looks quite heavyweight to me. So, if you don't care about performance, sure. But I'm clinging to the hope that we can make something faster, so I disagree for the moment :) > * It should be integrated with tracing and logging. Looks like two distinct things to me. Why couldn't you build a tracing + logging tool on top of it? > * custom tracing , error and log handlers should be possible and > dynamically configurable, Cheers, David -- David Rajchenbach-Teller, PhD Performance Team, Mozilla -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 487 bytes Desc: OpenPGP digital signature URL: From banderson at mozilla.com Thu May 24 13:39:45 2012 From: banderson at mozilla.com (Brian Anderson) Date: Thu, 24 May 2012 13:39:45 -0700 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FBE34B3.9060102@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> Message-ID: <4FBE9C91.9010603@mozilla.com> On 05/24/2012 06:16 AM, David Rajchenbach-Teller wrote: > On 5/24/12 12:40 PM, Bennie Kloosteman wrote: >>> * The system _must_ not prevent developers from calling C code from Rust. >>> * The system _must_ not prevent developers from passing a pointer to a >>> Rust function to C code that will call back to it. >>> * The system _must_ not prevent, some day, developers from calling Rust >>> from JavaScript. >>> * The system _must_ not prevent, some day, developers from calling >>> JavaScript from Rust. >> >> These should be unsafe - I believe this is the case with rust. > > I am not sure if you mean this in relation with all four points or with > the last one. So, indeed, I see no reason to change the fact that calls > to C are considered unsafe. Calls to C are considered safe currently, an arrangement I disagree with. -Brian From dteller at mozilla.com Fri May 25 02:54:11 2012 From: dteller at mozilla.com (David Rajchenbach-Teller) Date: Fri, 25 May 2012 11:54:11 +0200 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> Message-ID: <4FBF56C3.8020009@mozilla.com> Bennie, are you aware that you have replied to me only? On Fri May 25 06:28:01 2012, Bennie Kloosteman wrote: >> For calls to JavaScript, I have not given it much thought. I suppose >> that they could be either unsafe or somehow managed. > > externals calls either must work 100% ( including type conversions) or > be marked unsafe... if it breaks type or memory safety you need to say > that. Well, they could be managed (i.e. if you call JS, you receive a jsval, etc.), but I believe that's food for another thread. > Speaking of which we can handle C errors in calls but what about C++ > exception ? Does any C++ code we hook need exception wrappers ? > Also any error system rust should work when called from C ( unlike > C++ exceptions) . It was my understanding that direct interaction with C++ was out of the scope of Rust for the time being. >>>> * Issues _must_ not be restricted to integers (or to one single type). >> Interesting point. >> Are we talking about implementing Servo with a ring-like mechanism? >> >> If so, I suspect that we may simply not want this kind of issues to >> cross ring boundaries. Otherwise, if we let high-level issues cross >> boundaries, we could be sending closures, for instance, which sounds >> like a bad idea. >> >> For the moment, I believe that we should assume that the issue mechanism >> will not be used for crossing ring boundaries. > > Is rust or this error handling solely for Servo ? What about writing > say a kernel or Linux kernel modules in rust ? To me system > programming is where Rust should be. If its all working that's fine > but I was showing some horrible worst cases and its still nice to get > some error information rather than a horrible crash . Well, one of the main objectives of Rust is Servo. This does not mean that Rust should be restricted to Servo but, if we attempt to make Rust custom-tailored to too many things at start, increase the risk of ending up with a clumsy package. > A similar thing can occur with any external call to C or asm. But the > concept is the same try to get at least an error code returned even if > the most abysmal circumstances. >>>> * The default behavior in case of untreated issue _should_ be to >>>> gracefully kill the task or the application. >>> >>> If its integrated with tracing and logging the default behaviour >>> depends on the error type >>> >>> eg Trace , Log, Warning or Error continue , Critical = halt. >> >> A tracer/logger with this kind of semantics could certainly be built on >> top of the issue mechanism. I tend to believe that that they should be >> integrated from the start, though. > > Agree, I actually meant "should not", sorry :) More comments at the end of this message. >> Sounds interesting. I suspect (but I am not sure) that this is something >> that could/should be built on top of a more elementary issue mechanism. >> Could you provide a scenario that we could use as a working base? > > > Yes it probably should be built on top ..but I really like the model > where items received from a pipe/channel are broken out and fired as > events ( which multiple callers can hook). > > Note an interesting issue here , if the task is in a bad error state > it possibly may not use the "pipe/channel" for communication to the > parent .. How is this handled ? SIgnals ? Not sure I understand. Do you mean to handle a case in which the task is in such a bad shape (e.g. out of memory) that regular communication/failure semantics cannot be executed? >>>> * The system _should_ eventually have a low runtime cost ? in >>>> particular, the case in which no killing happens should be very fast. >>> >>> I don't think the performance of an error system is important at all , >>> its more important to have stack and meta data to resolve the issue >>> especially in debug builds ( and meta data is very slow) . This leads >>> to people using errors to pass/check state which is a bad habit >> >> Could you elaborate on that last sentence? > > int I = 0; > try{ > i = Int32.Parse( string) > } > catch (Exception ex) > { > // handle cant convert > } > > > vs > > int I = 0 ; > if ( Int32.TryParse ( &I , string) > { // handle cant convert } What's so wrong with the first snippet (assuming that we *can* have good performance even)? Note that it might be possible to come up with a mechanism that does both. Say, something along the lines of // Case 1: Just kill the task if we can't parse let i: int = Int32::parse(string, if_you_cant_do_it_just_fail); // Case 2: Let me check with errno let i: int = Int32::parse(string, if_you_cant_do_it_set_errno); if (errno) { ... } // Case 3: Variant of case 2 let i: int; alt (Int32.Parse(string, return_sum_type)) { {ok} {i = ok;} {ko} { // Handle case } } Now, I place this as an argument (giving it a nice type might be difficult) but this could of course be part of the language. I believe Graydon had something along these lines in mind. I'll try and rewrite that |move| function with this kind of parametric issue checking to see what it looks like. >> I am starting to believe that collecting [meta]data might be something >> that we want to configure on a usage basis. For instance, in function >> |move|, introduced a few e-mails ago (I just reposted it at >> https://gist.github.com/2781413 for convenience), we may be interested >> in the metadata of calls to lines 10 and 12, but gathering metadata for >> the calls in lines 2 and 6 is generally a waste of resources. > > Yes it should be optional eg you don't always want it on traces and > logs , most of the time on errors ( always on debug) and always on > critical errors. >> This looks quite heavyweight to me. So, if you don't care about >> performance, sure. But I'm clinging to the hope that we can make >> something faster, so I disagree for the moment :) > > It is heavy weight but most Rust errors will not have exception > scopes... just functions that return an errorno or pointerto an Error > object , Well, that isn't decided yet :) From the top of my head: - errorno is fast and crosses rings nicely, as you mentioned ? but it is quite difficult to extend and it is easy to forget checking it; - pointer to error is easier to extend, but more complicated wrt memory management, doesn't cross rings as nicely ? and it is just as easy to forget checking it. My hope is that we can think of a better mechanism with a large subset of the advantages of: - ML-style exceptions; - Erlang-style faults; - libc-style errno. > We want every possible error handled so the loop never stops ..no > matter what happens . eg an out of bounds array , null pointer or core > dump in an external call should kill the message but not the message > pump. Obviously the unsafe / external core dump is difficult and may > not be possible but it is desirable. > > So these are a counterpoint to fail , we could say failscope and fail . I don't get that last sentence. >>> * It should be integrated with tracing and logging. >> >> Looks like two distinct things to me. Why couldn't you build a tracing + >> logging tool on top of it? > > Above you say it should be integrated ? Sorry about the confusion, I meant "not integrated" :) > Yes it can be built in the > standard libs but It should still be integrated and more importantly > the logging and tracing considered in the design not in isolation , > look how hard it is in C , in theory every time you return a null or > non zero return code you should be logging / tracing . Well, this has two problems: - potentially awful performance; - insane amount of logs. In Gecko, until recently, the policy was to log every error at every step of the stack. This policy was changed a few months ago, because of log pollution. So that's an issue we need to consider. > Why does the caller need to do the logging ? You may have 1000s of > calls so you need to make wrappers just for the logging . malloc > should do it ( depending on app configuration) especially when logging > is built into the language. Since the standard lib doesn't the > caller has to do it ? The best place to log errors is the one that > generated the error and by integrating the error system with logging > it can be done automatically. > > > I have written a ton of code over the years ( when I bother in > quality programs) like catch (exception) { if blah blah / log / > trace ; if severity is < n rethrow } and the c style error code way > is abysmal here if you want to log . Yet 99% of the time all you want > is some sort of log everything if the severity is > N in a config > file concept without writing the code. Sounds like you need an error monad :) > Maybe something like : > > 1. Fail returns a ref to Error type it creates > 2. Stdlib Errors Return a ref to Error type ( which can be the > Error.Success so you can go if ( function() != Error.Success) > 3. When error type is created , error type has a hook for logging / > tracing on construction. by default this should log depending on a > configurable level. > 4. Most errors should be statically defined and allow strongly typed checking So you are advocating a style in which functions always (or almost always) return a status rather than a functional result, is that it? This is what we have in Gecko, but I tend to consider it more readable when: - the function returns its functional value; - errors are propagated by default. I like a lot the idea of attaching logging/tracing hooks to error types, though. > BTW this is an interesting article it covers .NET /C++ Exceptions vs > ErrorCodes vs COM HRESULT / IErrorInfo at the end > > http://blogs.msdn.com/b/cbrumme/archive/2003/10/01/51524.aspx Thanks for the pointer. Cheers, David -- David Rajchenbach-Teller, PhD Performance Team, Mozilla -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 487 bytes Desc: OpenPGP digital signature URL: From bklooste at gmail.com Fri May 25 03:00:26 2012 From: bklooste at gmail.com (Bennie Kloosteman) Date: Fri, 25 May 2012 18:00:26 +0800 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> Message-ID: On Thu, May 24, 2012 at 9:16 PM, David Rajchenbach-Teller wrote: > On 5/24/12 12:40 PM, Bennie Kloosteman wrote: >>> * The system _must_ not prevent developers from calling C code from Rust. >>> * The system _must_ not prevent developers from passing a pointer to a >>> Rust function to C code that will call back to it. >>> * The system _must_ not prevent, some day, developers from calling Rust >>> from JavaScript. >>> * The system _must_ not prevent, some day, developers from calling >>> JavaScript from Rust. >> >> These should be unsafe - I believe this is the case with rust. > > I am not sure if you mean this in relation with all four points or with > the last one. So, indeed, I see no reason to change the fact that calls > to C are considered unsafe. > > For calls to JavaScript, I have not given it much thought. I suppose > that they could be either unsafe or somehow managed. externals calls either must work 100% ( including type conversions) or be marked unsafe... if it breaks type or memory safety you need to say that. Speaking of which we can handle C errors in calls but what about C++ exceptions ? Does any C++ code we hook need exception wrappers ? Also any error system rust should work when called from C ?( unlike C++ exceptions) . > >>> * Issues _must_ not be restricted to integers (or to one single type). >> >> Yes ?but returning error codes and then looking up further issue >> detail separately is quite useful since your passing around little >> information it gives the highest chance in a partially unsafe >> environment of working out what happened . eg if the stack is >> corrupted or you have major issues its useful that the error is memory >> logged ?( eg in ring 0 in a ?rust based kernel ) and then retrieved . >> A simple ?integer return value has a much better chance.. > > Interesting point. > Are we talking about implementing Servo with a ring-like mechanism? > > If so, I suspect that we may simply not want this kind of issues to > cross ring boundaries. Otherwise, if we let high-level issues cross > boundaries, we could be sending closures, for instance, which sounds > like a bad idea. > > For the moment, I believe that we should assume that the issue mechanism > will not be used for crossing ring boundaries. Is rust or this error handling solely for Servo ? What about writing say a kernel or Linux kernel modules in rust ? To me system programming is where Rust should be. If its all working that's fine but ?I was showing some horrible worst cases and its still nice to get some error information rather than a horrible crash . A similar thing can occur with any external call to C or asm. But the concept is the same try to get at least an error code returned even if the most abysmal circumstances. > >> This could be a 64 bit ?value with the low 32 bits the error and the >> upper 32 bit some sort of hash code. >> >> A more detailed but not guaranteed representation should be a detailed >> structure. with time stamps etc ?I would even say this detail should >> be created asynchronously via a new task while the actual error is >> returned immediately . >> >> errno is a pain , ?but not because it returns an int but because all >> the information you can retrieve on it is a error string. >> >> Actually you could log all the detail and just reference an index to >> the log , Though this would require recent log entries to be >> recallable and have more detail in the log fields eg error category >> and level ?( which are useful in a log anyway) > > All these are interesting points, although perhaps they arrive a little > too early in the thread :) > Im not so young anymore so when I train of thoughts I like to get it on E-paper ;-) >>> * The default behavior in case of untreated issue _should_ be to >>> gracefully kill the task or the application. >> >> If its integrated with tracing and logging the default behaviour >> depends on the error type >> >> eg Trace ?, Log, Warning or Error continue , Critical ?= halt. > > A tracer/logger with this kind of semantics could certainly be built on > top of the issue mechanism. I tend to believe that that they should be > integrated from the start, though. Agree, > >>> * Whenever an untreated issue kills a task/application, it _should_ >>> produces a report usable by the developer for fixing the issue. >>> * It _should_ be possible to deactivate that killing behavior. There >>> _may_ be limitations. >>> * It _should_ be possible to deactivate that killing behavior >>> conditionally (i.e. only for some errors). >> >> also a task can monitor child tasks for errors ( I don't think Rust >> has events but something like that) and choose on error to abort or >> continue depending on the error condition. . > > Sounds interesting. I suspect (but I am not sure) that this is something > that could/should be built on top of a more elementary issue mechanism. > Could you provide a scenario that we could use as a working base? Yes it probably should be built on top ..but I really like the model where items received from a pipe/channel are broken out and fired as events ( which multiple callers can hook). Note an interesting issue here , if the task ?is in a bad error state it ?possibly may not use the "pipe/channel" for communication to the parent .. How is this handled ? SIgnals ? > > >>> * The system _should_ eventually have a low runtime cost ? in >>> particular, the case in which no killing happens should be very fast. >> >> I don't think the performance of an error system is important at all , >> its more important to have stack and meta data to resolve the issue >> especially in debug builds ( and meta data is very slow) . ?This leads >> to people using errors to pass/check ?state which is a bad habit > > Could you elaborate on that last sentence? int I = 0; try{ ? ?i = Int32.Parse( string) } catch (Exception ex) { ?// handle cant convert } vs int I ?= 0 ; if ( Int32.TryParse ( &I , string) ? ? { ?// handle cant convert } > > I am starting to believe that collecting [meta]data might be something > that we want to configure on a usage basis. For instance, in function > |move|, introduced a few e-mails ago (I just reposted it at > https://gist.github.com/2781413 for convenience), we may be interested > in the metadata of calls to lines 10 and 12, but gathering metadata for > the calls in lines 2 and 6 is generally a waste of resources. Yes it should be optional eg you don't always want it on traces and logs , most of the time on errors ( always on debug) and always on critical errors. > > If we think of this as exceptions, it could be that we want several the > ability to specify if/when we want to gather metadata at the level of an > equivalent of |try|. I believe that this fits nicely with what Graydon > had in mind, by the way. > >> I would add >> * Tasks are the error scope / unit >> >> * should we have an exception like syntax that automatically creates a >> sub task for the embedded statements ( and hence means those >> statements cannot result in failure of the parent tasks) . Since >> errors are scoped to the task , the easy way is a new task . > > This looks quite heavyweight to me. So, if you don't care about > performance, sure. But I'm clinging to the hope that we can make > something faster, so I disagree for the moment :) It is heavy weight but most Rust errors will not have exception scopes... just functions that return an errorno or pointerto an Error object , ?The concept of exception scope is useful though eg a single threaded message pump is normally foreach ( var message in buffer) { ?try { ? ? ? ?ProcessMessage( message); } catch (ThreadAbortException ex) { // handle } catch (Exception ex) { // handle and continue to next message } } We want every possible error handled so the loop never stops ?..no matter what happens . eg an out of bounds array , null pointer or core dump in an external call ?should kill the message but not the message pump. ?Obviously the unsafe / external core dump is difficult and may not be possible but it is desirable. So these are a counterpoint to fail , we could say failscope and fail . >> * It should be integrated with tracing and logging. > > Looks like two distinct things to me. Why couldn't you build a tracing + > logging tool on top of it? Above you say it should be integrated ? Yes it can be built in the standard libs but It should still be integrated ?and more importantly the logging and tracing considered ?in the design not in isolation , look how hard it is in C ?, in theory every time you return a null or non zero return code you should be logging / tracing . ie ?ptr = malloc(N); if ( ptr == NULL) { ? ?//get ?error no ? ?// log / trace ? ?// return to user } Why does the caller need to do the logging ?? You may have 1000s of calls ?so you need to make wrappers just for the logging . malloc should do it ( depending on app configuration) especially when logging is built into the language. ?Since the standard lib doesn't ?the caller has to do it ? The best place to log errors is the one that generated the error and by integrating the error system with logging it can be done automatically. I have written a ton of code over the years ?( when I bother in quality programs) like catch (exception) ? { if blah blah / log ?/ trace ; if severity is ?< n rethrow } and the c style error code way is abysmal here ?if you want to log . Yet 99% of the time all you want is some sort of log everything if the severity is > N ?in a config file concept without writing the code. Maybe something like : 1. Fail ?returns a ref to ?Error type it creates 2. Stdlib Errors Return a ref to Error type ?( which can be the Error.Success ? so you can go if ( function() != Error.Success) 3. When error type is created , error type has a hook for logging / tracing on construction. by default this should log depending on a configurable level. 4. Most errors should be statically defined and allow strongly typed checking BTW this is an interesting article it covers .NET /C++ Exceptions vs ErrorCodes vs COM HRESULT / IErrorInfo at the end http://blogs.msdn.com/b/cbrumme/archive/2003/10/01/51524.aspx Ben From bklooste at gmail.com Fri May 25 04:41:08 2012 From: bklooste at gmail.com (Bennie Kloosteman) Date: Fri, 25 May 2012 19:41:08 +0800 Subject: [rust-dev] Eliminating Bounds checks on arrays Message-ID: Does Rust have bounds checking on arrays ? I was on the bitc list and Shap suggested rather than javas complicated ( and expensive from an engineering point of view) bounds checking elimination in a Haskell typesystem you can use a Natural number 1:N kind for the length. This is a really cool technique , it makes the type system pick up the bounds checks. Here is Wren description ... " Consider the type of vectors of fixed length, where the length is annotated in the type: Vec : Nat -> Type -> Type So (Vec N A) is the type of length-N vectors with elements of type A. Now, in addition we must have some canonical finite sets. We will also annotate the size of these finite sets in their type: Fin : Nat -> Type where (Fin n) denotes the set of all natural numbers smaller than n. Now, indexing into these vectors does not need any dynamic bounds checks at all. Just use the following function for indexing: (!) : forall n a. Vec n a -> Fin n -> a If the following expression is well-typed, x!i Then we know, exists (a : Type) exists (n : Nat) x : Vec n a i : Fin n Which means we also know that, 0 <= fromFin i < n where fromFin is the natural embedding of any Fin type into regular nats/ints. Therefore, after type checking, the dynamic value of i must be in bounds for x. You can play this trick to a limited extent in any language with a sufficiently interesting type system. But in order to really get mileage out of it, you're going to want things like addition and multiplication at the type level. Also, you're going to want to group all the type-level numbers together into a Nat kind, in order to rule out nonsense like (Vec Int A). -- Live well, ~wren " From me at kevincantu.org Fri May 25 08:43:05 2012 From: me at kevincantu.org (Kevin Cantu) Date: Fri, 25 May 2012 08:43:05 -0700 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> Message-ID: This conversation reminds me of Alexandrescu's talk about D's scope keyword: http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2012/Three-Unlikely-Successful-Features-of-D It looks like a graceful syntax for handling all sorts of nested error and failure cleanup... On May 25, 2012 3:00 AM, "Bennie Kloosteman" wrote: > On Thu, May 24, 2012 at 9:16 PM, David Rajchenbach-Teller > wrote: > > On 5/24/12 12:40 PM, Bennie Kloosteman wrote: > >>> * The system _must_ not prevent developers from calling C code from > Rust. > >>> * The system _must_ not prevent developers from passing a pointer to a > >>> Rust function to C code that will call back to it. > >>> * The system _must_ not prevent, some day, developers from calling Rust > >>> from JavaScript. > >>> * The system _must_ not prevent, some day, developers from calling > >>> JavaScript from Rust. > >> > >> These should be unsafe - I believe this is the case with rust. > > > > I am not sure if you mean this in relation with all four points or with > > the last one. So, indeed, I see no reason to change the fact that calls > > to C are considered unsafe. > > > > For calls to JavaScript, I have not given it much thought. I suppose > > that they could be either unsafe or somehow managed. > > externals calls either must work 100% ( including type conversions) or > be marked unsafe... if it breaks type or memory safety you need to say > that. > > Speaking of which we can handle C errors in calls but what about C++ > exceptions ? Does any C++ code we hook need exception wrappers ? > Also any error system rust should work when called from C ( unlike > C++ exceptions) . > > > > > >>> * Issues _must_ not be restricted to integers (or to one single type). > >> > >> Yes but returning error codes and then looking up further issue > >> detail separately is quite useful since your passing around little > >> information it gives the highest chance in a partially unsafe > >> environment of working out what happened . eg if the stack is > >> corrupted or you have major issues its useful that the error is memory > >> logged ( eg in ring 0 in a rust based kernel ) and then retrieved . > >> A simple integer return value has a much better chance.. > > > > Interesting point. > > Are we talking about implementing Servo with a ring-like mechanism? > > > > If so, I suspect that we may simply not want this kind of issues to > > cross ring boundaries. Otherwise, if we let high-level issues cross > > boundaries, we could be sending closures, for instance, which sounds > > like a bad idea. > > > > For the moment, I believe that we should assume that the issue mechanism > > will not be used for crossing ring boundaries. > > Is rust or this error handling solely for Servo ? What about writing > say a kernel or Linux kernel modules in rust ? To me system > programming is where Rust should be. If its all working that's fine > but I was showing some horrible worst cases and its still nice to get > some error information rather than a horrible crash . > > A similar thing can occur with any external call to C or asm. But the > concept is the same try to get at least an error code returned even if > the most abysmal circumstances. > > > > >> This could be a 64 bit value with the low 32 bits the error and the > >> upper 32 bit some sort of hash code. > >> > >> A more detailed but not guaranteed representation should be a detailed > >> structure. with time stamps etc I would even say this detail should > >> be created asynchronously via a new task while the actual error is > >> returned immediately . > >> > >> errno is a pain , but not because it returns an int but because all > >> the information you can retrieve on it is a error string. > >> > >> Actually you could log all the detail and just reference an index to > >> the log , Though this would require recent log entries to be > >> recallable and have more detail in the log fields eg error category > >> and level ( which are useful in a log anyway) > > > > All these are interesting points, although perhaps they arrive a little > > too early in the thread :) > > > > Im not so young anymore so when I train of thoughts I like to get it > on E-paper ;-) > > >>> * The default behavior in case of untreated issue _should_ be to > >>> gracefully kill the task or the application. > >> > >> If its integrated with tracing and logging the default behaviour > >> depends on the error type > >> > >> eg Trace , Log, Warning or Error continue , Critical = halt. > > > > A tracer/logger with this kind of semantics could certainly be built on > > top of the issue mechanism. I tend to believe that that they should be > > integrated from the start, though. > > Agree, > > > >>> * Whenever an untreated issue kills a task/application, it _should_ > >>> produces a report usable by the developer for fixing the issue. > >>> * It _should_ be possible to deactivate that killing behavior. There > >>> _may_ be limitations. > >>> * It _should_ be possible to deactivate that killing behavior > >>> conditionally (i.e. only for some errors). > >> > >> also a task can monitor child tasks for errors ( I don't think Rust > >> has events but something like that) and choose on error to abort or > >> continue depending on the error condition. . > > > > Sounds interesting. I suspect (but I am not sure) that this is something > > that could/should be built on top of a more elementary issue mechanism. > > Could you provide a scenario that we could use as a working base? > > > Yes it probably should be built on top ..but I really like the model > where items received from a pipe/channel are broken out and fired as > events ( which multiple callers can hook). > > Note an interesting issue here , if the task is in a bad error state > it possibly may not use the "pipe/channel" for communication to the > parent .. How is this handled ? SIgnals ? > > > > > > >>> * The system _should_ eventually have a low runtime cost ? in > >>> particular, the case in which no killing happens should be very fast. > >> > >> I don't think the performance of an error system is important at all , > >> its more important to have stack and meta data to resolve the issue > >> especially in debug builds ( and meta data is very slow) . This leads > >> to people using errors to pass/check state which is a bad habit > > > > Could you elaborate on that last sentence? > > int I = 0; > try{ > i = Int32.Parse( string) > } > catch (Exception ex) > { > // handle cant convert > } > > > vs > > int I = 0 ; > if ( Int32.TryParse ( &I , string) > { // handle cant convert } > > > > > > I am starting to believe that collecting [meta]data might be something > > that we want to configure on a usage basis. For instance, in function > > |move|, introduced a few e-mails ago (I just reposted it at > > https://gist.github.com/2781413 for convenience), we may be interested > > in the metadata of calls to lines 10 and 12, but gathering metadata for > > the calls in lines 2 and 6 is generally a waste of resources. > > Yes it should be optional eg you don't always want it on traces and > logs , most of the time on errors ( always on debug) and always on > critical errors. > > > > > If we think of this as exceptions, it could be that we want several the > > ability to specify if/when we want to gather metadata at the level of an > > equivalent of |try|. I believe that this fits nicely with what Graydon > > had in mind, by the way. > > > >> I would add > >> * Tasks are the error scope / unit > >> > >> * should we have an exception like syntax that automatically creates a > >> sub task for the embedded statements ( and hence means those > >> statements cannot result in failure of the parent tasks) . Since > >> errors are scoped to the task , the easy way is a new task . > > > > This looks quite heavyweight to me. So, if you don't care about > > performance, sure. But I'm clinging to the hope that we can make > > something faster, so I disagree for the moment :) > > It is heavy weight but most Rust errors will not have exception > scopes... just functions that return an errorno or pointerto an Error > object , The concept of exception scope is useful though eg a single > threaded message pump is normally > > foreach ( var message in buffer) > { > try { > ProcessMessage( message); > } > catch (ThreadAbortException ex) { // handle } > catch (Exception ex) { // handle and continue to next message } > } > > We want every possible error handled so the loop never stops ..no > matter what happens . eg an out of bounds array , null pointer or core > dump in an external call should kill the message but not the message > pump. Obviously the unsafe / external core dump is difficult and may > not be possible but it is desirable. > > So these are a counterpoint to fail , we could say failscope and fail . > > > > >> * It should be integrated with tracing and logging. > > > > Looks like two distinct things to me. Why couldn't you build a tracing + > > logging tool on top of it? > > Above you say it should be integrated ? Yes it can be built in the > standard libs but It should still be integrated and more importantly > the logging and tracing considered in the design not in isolation , > look how hard it is in C , in theory every time you return a null or > non zero return code you should be logging / tracing . > > ie ptr = malloc(N); > if ( ptr == NULL) > { > //get error no > // log / trace > // return to user > } > > Why does the caller need to do the logging ? You may have 1000s of > calls so you need to make wrappers just for the logging . malloc > should do it ( depending on app configuration) especially when logging > is built into the language. Since the standard lib doesn't the > caller has to do it ? The best place to log errors is the one that > generated the error and by integrating the error system with logging > it can be done automatically. > > > I have written a ton of code over the years ( when I bother in > quality programs) like catch (exception) { if blah blah / log / > trace ; if severity is < n rethrow } and the c style error code way > is abysmal here if you want to log . Yet 99% of the time all you want > is some sort of log everything if the severity is > N in a config > file concept without writing the code. > > Maybe something like : > > 1. Fail returns a ref to Error type it creates > 2. Stdlib Errors Return a ref to Error type ( which can be the > Error.Success so you can go if ( function() != Error.Success) > 3. When error type is created , error type has a hook for logging / > tracing on construction. by default this should log depending on a > configurable level. > 4. Most errors should be statically defined and allow strongly typed > checking > > BTW this is an interesting article it covers .NET /C++ Exceptions vs > ErrorCodes vs COM HRESULT / IErrorInfo at the end > > http://blogs.msdn.com/b/cbrumme/archive/2003/10/01/51524.aspx > > > Ben > _______________________________________________ > Rust-dev mailing list > Rust-dev at mozilla.org > https://mail.mozilla.org/listinfo/rust-dev > -------------- next part -------------- An HTML attachment was scrubbed... URL: From dteller at mozilla.com Fri May 25 08:46:12 2012 From: dteller at mozilla.com (David Rajchenbach-Teller) Date: Fri, 25 May 2012 17:46:12 +0200 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> Message-ID: <4FBFA944.3080504@mozilla.com> On 5/25/12 5:43 PM, Kevin Cantu wrote: > This conversation reminds me of Alexandrescu's talk about D's scope keyword: > http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2012/Three-Unlikely-Successful-Features-of-D > > It looks like a graceful syntax for handling all sorts of nested error > and failure cleanup... Do you know if there is a paper version of this talk? Cheers, David -- David Rajchenbach-Teller, PhD Performance Team, Mozilla -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 487 bytes Desc: OpenPGP digital signature URL: From pwalton at mozilla.com Fri May 25 09:01:25 2012 From: pwalton at mozilla.com (Patrick Walton) Date: Fri, 25 May 2012 09:01:25 -0700 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> Message-ID: <4FBFACD5.5040405@mozilla.com> On 05/25/2012 08:43 AM, Kevin Cantu wrote: > This conversation reminds me of Alexandrescu's talk about D's scope keyword: > http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2012/Three-Unlikely-Successful-Features-of-D > > It looks like a graceful syntax for handling all sorts of nested error > and failure cleanup... I like the scope keyword, FWIW. It'd be even better if you didn't have to provide a variable name if all you want to do is execute some code at the end of the block. This would provide a facility like Go's "defer" keyword, but more general since it also admits C++ RAII patterns. Patrick From dteller at mozilla.com Fri May 25 10:16:04 2012 From: dteller at mozilla.com (David Rajchenbach-Teller) Date: Fri, 25 May 2012 19:16:04 +0200 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FBFACD5.5040405@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> <4FBFACD5.5040405@mozilla.com> Message-ID: <4FBFBE54.2080008@mozilla.com> On Fri May 25 18:01:25 2012, Patrick Walton wrote: > On 05/25/2012 08:43 AM, Kevin Cantu wrote: >> This conversation reminds me of Alexandrescu's talk about D's scope >> keyword: >> http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2012/Three-Unlikely-Successful-Features-of-D >> >> >> It looks like a graceful syntax for handling all sorts of nested error >> and failure cleanup... > > I like the scope keyword, FWIW. It'd be even better if you didn't have > to provide a variable name if all you want to do is execute some code > at the end of the block. > > This would provide a facility like Go's "defer" keyword, but more > general since it also admits C++ RAII patterns. > > Patrick What's the difference between |scope| and Rust's resources, exactly? Cheers, David -- David Rajchenbach-Teller, PhD Performance Team, Mozilla -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 487 bytes Desc: OpenPGP digital signature URL: From matthieu.monrocq at gmail.com Fri May 25 10:21:15 2012 From: matthieu.monrocq at gmail.com (Matthieu Monrocq) Date: Fri, 25 May 2012 19:21:15 +0200 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FBFBE54.2080008@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> <4FBFACD5.5040405@mozilla.com> <4FBFBE54.2080008@mozilla.com> Message-ID: On Fri, May 25, 2012 at 7:16 PM, David Rajchenbach-Teller < dteller at mozilla.com> wrote: > On Fri May 25 18:01:25 2012, Patrick Walton wrote: > > On 05/25/2012 08:43 AM, Kevin Cantu wrote: > >> This conversation reminds me of Alexandrescu's talk about D's scope > >> keyword: > >> > http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2012/Three-Unlikely-Successful-Features-of-D > >> > >> > >> It looks like a graceful syntax for handling all sorts of nested error > >> and failure cleanup... > > > > I like the scope keyword, FWIW. It'd be even better if you didn't have > > to provide a variable name if all you want to do is execute some code > > at the end of the block. > > > > This would provide a facility like Go's "defer" keyword, but more > > general since it also admits C++ RAII patterns. > > > > Patrick > > What's the difference between |scope| and Rust's resources, exactly? > > Cheers, > David > > -- > David Rajchenbach-Teller, PhD > Performance Team, Mozilla > > > Regarding adding logs to the errors: - Boost.Exception has something similar: you can add class instances to the exception using the error info mechanism [1] - It also reminds me of what happens in case of failures using the "note" expressions, I believe the same notes could be reused in case of exceptions/errors to provide additional logs. Of course, there is a difference between the two schemes. Boost's is somewhat more powerful because it does not consist of adding simple strings but full-blown objects (which could be conditionned to be printable), and thus allow inspection of structured data at the error-handling site. It may be thought of as overkill too... Regarding D's scope keyword [2] There are several statements based on it: - scope(exit) xxxx where xxxx is executed on exit, no matter what - scope(failure) xxxx where xxxx is executed on exit if the previous statement failed - scope(success) xxxx where xxxx is executed on exit if the previous statement succeeded On the other hand, it kind of look like a hack, maybe it is an issue of getting used to it though. [1]: http://www.boost.org/doc/libs/1_49_0/libs/exception/doc/error_info.html [2]: http://dlang.org/exception-safe.html -------------- next part -------------- An HTML attachment was scrubbed... URL: From pwalton at mozilla.com Fri May 25 10:28:14 2012 From: pwalton at mozilla.com (Patrick Walton) Date: Fri, 25 May 2012 10:28:14 -0700 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FBFBE54.2080008@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> <4FBFACD5.5040405@mozilla.com> <4FBFBE54.2080008@mozilla.com> Message-ID: <4FBFC12E.2090902@mozilla.com> On 5/25/12 10:16 AM, David Rajchenbach-Teller wrote: > What's the difference between |scope| and Rust's resources, exactly? "scope" would be executed unconditionally at the end of the current block, while the rules for standard Rust RAII are somewhat more complex and depend on the initedness of the variable, whether it was moved, etc. There's a hidden dynamic flag created by the compiler and set I've been toying with the idea of changing standard Rust RAII to "execute this when the variable goes dead" (in the classic compiler liveness sense) and introduce a "scope" keyword for something more like C++ or D RAII in which the liveness of the variable is restricted to be exactly the extent of the block, making it safe to unconditionally run the destructor once the variable goes out of scope. Patrick From pwalton at mozilla.com Fri May 25 10:29:04 2012 From: pwalton at mozilla.com (Patrick Walton) Date: Fri, 25 May 2012 10:29:04 -0700 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FBFC12E.2090902@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> <4FBFACD5.5040405@mozilla.com> <4FBFBE54.2080008@mozilla.com> <4FBFC12E.2090902@mozilla.com> Message-ID: <4FBFC160.6030009@mozilla.com> On 5/25/12 10:28 AM, Patrick Walton wrote: > There's a hidden dynamic flag created by the compiler Hit send too early. There's a hidden dynamic flag created by the compiler to track initedness for each variable. Patrick From niko at alum.mit.edu Fri May 25 10:45:04 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Fri, 25 May 2012 10:45:04 -0700 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FBFC12E.2090902@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> <4FBFACD5.5040405@mozilla.com> <4FBFBE54.2080008@mozilla.com> <4FBFC12E.2090902@mozilla.com> Message-ID: <4FBFC520.4090000@alum.mit.edu> On 5/25/12 10:28 AM, Patrick Walton wrote: > I've been toying with the idea of changing standard Rust RAII to > "execute this when the variable goes dead" (in the classic compiler > liveness sense) and introduce a "scope" keyword for something more > like C++ or D RAII in which the liveness of the variable is restricted > to be exactly the extent of the block, making it safe to > unconditionally run the destructor once the variable goes out of scope. It seems like the official rule could be as simple as "the destructor is executed at some point after the last access and before the exit from the scope". This would give us more freedom. I don't know the details of the D version, but I like the idea of scope as it makes it more explicit when you are creating a resource just for side-effects. It also avoids the need for unused variables. For example, this code that is sprinkled throughout trans: let _icx = cx.insn_ctxt("trans_local_var"); would become something like: scope cx.insn_ctxt("trans_local_var"); which seems much clearer to me. Niko From banderson at mozilla.com Fri May 25 11:18:04 2012 From: banderson at mozilla.com (Brian Anderson) Date: Fri, 25 May 2012 11:18:04 -0700 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> Message-ID: <4FBFCCDC.3010507@mozilla.com> On 05/25/2012 03:00 AM, Bennie Kloosteman wrote: > On Thu, May 24, 2012 at 9:16 PM, David Rajchenbach-Teller > wrote: >> On 5/24/12 12:40 PM, Bennie Kloosteman wrote: >>>> * The system _must_ not prevent developers from calling C code from Rust. >>>> * The system _must_ not prevent developers from passing a pointer to a >>>> Rust function to C code that will call back to it. >>>> * The system _must_ not prevent, some day, developers from calling Rust >>>> from JavaScript. >>>> * The system _must_ not prevent, some day, developers from calling >>>> JavaScript from Rust. >>> >>> These should be unsafe - I believe this is the case with rust. >> >> I am not sure if you mean this in relation with all four points or with >> the last one. So, indeed, I see no reason to change the fact that calls >> to C are considered unsafe. >> >> For calls to JavaScript, I have not given it much thought. I suppose >> that they could be either unsafe or somehow managed. > > externals calls either must work 100% ( including type conversions) or > be marked unsafe... if it breaks type or memory safety you need to say > that. > > Speaking of which we can handle C errors in calls but what about C++ > exceptions ? Does any C++ code we hook need exception wrappers ? > Also any error system rust should work when called from C ( unlike > C++ exceptions) . The current situation is that C++ code must not throw into Rust code or the runtime will abort and Rust code that has been called from native code must not fail or the runtime will abort. We can probably come up with some way to handle both these. -Brian From bklooste at gmail.com Fri May 25 20:55:24 2012 From: bklooste at gmail.com (Bennie Kloosteman) Date: Sat, 26 May 2012 11:55:24 +0800 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FBFC12E.2090902@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> <4FBFACD5.5040405@mozilla.com> <4FBFBE54.2080008@mozilla.com> <4FBFC12E.2090902@mozilla.com> Message-ID: I thought Rust was not going to implement C++ style exceptions . are these scopes not very similar ? Do you unwind the stack etc when you are nested deeply within a scope and a fail occurs ? Also more importantly can you safely handle tasks that were created in the now to be unwound scopes ? I don't know about the internals of Rust Tasks but yanking the stack when a sub task was created from it and is running on another thread could get tricky. This was the main reason I was thinking scopes should be a task underneath while its heavy it would not be used in most cases. In most case use a Com Hresult/ IErrorInfo scheme ( which can hook into logging) , when you do need guaranteed unwinding use a scope ( or fail scope) based on a task behind the scenes. To me it fits nicely into the ideas of the language. Ben > "scope" would be executed unconditionally at the end of the current block, > while the rules for standard Rust RAII are somewhat more complex and depend > on the initedness of the variable, whether it was moved, etc. There's a > hidden dynamic flag created by the compiler and set > From benjamin.kircher at gmail.com Sat May 26 16:38:18 2012 From: benjamin.kircher at gmail.com (Benjamin Kircher) Date: Sun, 27 May 2012 01:38:18 +0200 Subject: [rust-dev] Passing structures to C functions and Rust records Message-ID: Hi. This is my first mail to this list, so hello all. I am doing C++ during daytime for a small company in Germany since nearly 4 years now but I wrestle with that language a bit longer (since the Visual Studio 6.0 days, uh), well, and never really did a lot of C. There are a few things about Rust I must say I really like. I am poking around with rustc since a few weeks, having some fun in the late hours after work. So much for the introduction. The question: I have a typical C function that takes a pointer to a struct as argument. Since a Rust record is binary compatible with a C struct I can do something like this: type stat = { ? ?st_dev: dev_t, ? ? ? ? // ID of device containing file ? ?st_ino: ino_t, ? ? ? ? // inode number ? ?st_mode: mode_t, ? ? ? // protection ? ?st_nlink: nlink_t, ? ? // number of hard links ? ?st_uid: uid_t, ? ? ? ? // user ID of owner ? ?st_gid: gid_t, ? ? ? ? // group ID of owner ? ?st_rdev: dev_t, ? ? ? ?// device ID (if special file) ? ?st_size: off_t, ? ? ? ?// total size, in bytes ? ?st_blksize: blksize_t, // block size for file system I/O ? ?st_blocks: blkcnt_t, ? // number of blocks allocated ? ?st_atime: time_t, ? ? ?// time of last access ? ?st_mtime: time_t, ? ? ?// time of last modification ? ?st_ctime: time_t ? ? ? // time of last status change }; #[link_name = "c"] native mod sys { ? ?// standard C library is already linked with Rust programs ? ?#[nolink] ? ?fn stat(filename: *c_char, buf: *stat) -> c_int; } This might compile and link fine on a POSIX system. However, since I don't have to include any header like with Rust I can do something like this: type stat = { garbage: c_int, // something wrong here }; fn create_stat() -> stat { ret { garbage: 0 as c_int } } #[link_name = "c"] native mod sys { #[nolink] fn stat(filename: *c_char, buf: *stat) -> c_int; } fn main() { let s = "README.txt"; let buf = create_stat(); str::as_c_str(s, {|nbuf| sys::stat(nbuf, ptr::addr_of(buf)) }); } Which will compile and run ...and probably segfault very soon. While declaring a record in a Rust program is very straightforward it is cumbersome to retype all the information already present in C header files, if not dangerous when the structures are not as expected at runtime. Is there any way to actually get the declaration from a C header in Rust, i.e. by using a #include statement or to get any compile time error when the types are not as expected? Am I missing something? Benjamin From banderson at mozilla.com Sat May 26 18:51:25 2012 From: banderson at mozilla.com (Brian Anderson) Date: Sat, 26 May 2012 18:51:25 -0700 Subject: [rust-dev] Passing structures to C functions and Rust records In-Reply-To: References: Message-ID: <4FC1889D.7080202@mozilla.com> On 05/26/2012 04:38 PM, Benjamin Kircher wrote: > Hi. > > This is my first mail to this list, so hello all. I am doing C++ > during daytime for a small company in Germany since nearly 4 years now > but I wrestle with that language a bit longer (since the Visual Studio > 6.0 days, uh), well, and never really did a lot of C. There are a few > things about Rust I must say I really like. I am poking around with > rustc since a few weeks, having some fun in the late hours after work. > So much for the introduction. Welcome! > While declaring a record in a Rust program is very straightforward it > is cumbersome to retype all the information already present in C > header files, if not dangerous when the structures are not as expected > at runtime. Is there any way to actually get the declaration from a C > header in Rust, i.e. by using a #include statement or to get any > compile time error when the types are not as expected? Am I missing > something? There is a project called bindgen[1] that is going to become the official way to do C bindings. You run it against some C header files and it emits Rust source code with the proper declarations. Eventually it will be integrated into the main rust distribution as a tool and/or compiler pass. Right now it's not quite user-friendly and it sometimes generates things that need manual tweaking, but I do use it for all my native bindings now. Here are some projects that are using bindgen (see the gen.py files for examples of running the tool): * https://github.com/brson/rust-mozjs * https://github.com/brson/rust-azure * https://github.com/brson/rust-harfbuzz -Brian [1] http://github.com/crabtw/rust-bindgen From niko at alum.mit.edu Sat May 26 18:57:16 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Sat, 26 May 2012 18:57:16 -0700 Subject: [rust-dev] Eliminating Bounds checks on arrays In-Reply-To: References: Message-ID: <4FC189FC.4020500@alum.mit.edu> For the moment, we are trying to avoid introducing arbitrary arithmetic into the type system. It's possible typestate could be used here in some limited cases. Niko On 5/25/12 4:41 AM, Bennie Kloosteman wrote: > Does Rust have bounds checking on arrays ? I was on the bitc list and > Shap suggested rather than javas complicated ( and expensive from an > engineering point of view) bounds checking elimination in a Haskell > typesystem you can use a Natural number 1:N kind for the length. > This is a really cool technique , it makes the type system pick up the > bounds checks. > > Here is Wren description ... > " > Consider the type of vectors of fixed length, where the length is > annotated in the type: > > Vec : Nat -> Type -> Type > > So (Vec N A) is the type of length-N vectors with elements of type A. Now, > in addition we must have some canonical finite sets. We will also annotate > the size of these finite sets in their type: > > Fin : Nat -> Type > > where (Fin n) denotes the set of all natural numbers smaller than n. Now, > indexing into these vectors does not need any dynamic bounds checks at > all. Just use the following function for indexing: > > (!) : forall n a. Vec n a -> Fin n -> a > > If the following expression is well-typed, > > x!i > > Then we know, > > exists (a : Type) > exists (n : Nat) > x : Vec n a > i : Fin n > > Which means we also know that, > > 0<= fromFin i< n > > where fromFin is the natural embedding of any Fin type into regular > nats/ints. Therefore, after type checking, the dynamic value of i must be > in bounds for x. > > > You can play this trick to a limited extent in any language with a > sufficiently interesting type system. But in order to really get mileage > out of it, you're going to want things like addition and multiplication at > the type level. Also, you're going to want to group all the type-level > numbers together into a Nat kind, in order to rule out nonsense like (Vec > Int A). > > -- > Live well, > ~wren > > " > _______________________________________________ > Rust-dev mailing list > Rust-dev at mozilla.org > https://mail.mozilla.org/listinfo/rust-dev From pwalton at mozilla.com Sat May 26 19:15:22 2012 From: pwalton at mozilla.com (Patrick Walton) Date: Sat, 26 May 2012 19:15:22 -0700 Subject: [rust-dev] Eliminating Bounds checks on arrays In-Reply-To: <4FC189FC.4020500@alum.mit.edu> References: <4FC189FC.4020500@alum.mit.edu> Message-ID: <4FC18E3A.9040104@mozilla.com> On 05/26/2012 06:57 PM, Niko Matsakis wrote: > For the moment, we are trying to avoid introducing arbitrary arithmetic > into the type system. It's possible typestate could be used here in some > limited cases. And I suspect LLVM can hoist out a bunch of bounds checks if we provide it with type-based alias analysis. Patrick From niko at alum.mit.edu Mon May 28 14:54:54 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Mon, 28 May 2012 14:54:54 -0700 Subject: [rust-dev] agenda Message-ID: <4FC3F42E.1040405@alum.mit.edu> https://etherpad.mozilla.org/Meeting-weekly-2012-05-29 From dteller at mozilla.com Tue May 29 01:07:12 2012 From: dteller at mozilla.com (David Rajchenbach-Teller) Date: Tue, 29 May 2012 10:07:12 +0200 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FBE34B3.9060102@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> Message-ID: <4FC483B0.9010004@mozilla.com> As promised, I have attempted to rewrite my original function |move| with a mechanism that would let us define issue-handling policies. This mechanism is inspired by both the current thread and the previous thread on exceptions, a few months ago. So far, the result is not too convincing yet, but it can certainly serve as a base for improvements: https://gist.github.com/2823196 -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 487 bytes Desc: OpenPGP digital signature URL: From niko at alum.mit.edu Tue May 29 07:57:58 2012 From: niko at alum.mit.edu (Niko Matsakis) Date: Tue, 29 May 2012 07:57:58 -0700 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FC483B0.9010004@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> <4FC483B0.9010004@mozilla.com> Message-ID: <4FC4E3F6.1080201@alum.mit.edu> This code seems wrong... what if an error occurs as part of the call to `copy()`, for example? As a kind of experience report, I have been using `result` types for those parts of the compiler where errors must be recoverable rather than just being reported to the user and papered over. I have found it's pretty nice except that writing first_thing().chain { |res1| second_thing().chain { |res2| ok(infallible_combination_of(res1, res2)) } } gets a little old and induces horrible rightward drift. A macro along the lines of Haskell's do notation would address this: #do { let res1 = first_thing(); let res2 = second_thing(); infallible_combination_of(res1, res2) } In general it seems there are three classes of errors - globally unrecoverable (just call `fail`) - locally unrecoverable (return a `result` type) - recoverable (pass in handler or error reporter) Globally unrecoverable means that the whole task cannot continue (this is clearly not appropriate for libraries). Locally recoverable means that some subset of the program must abort, but some outer handler could possibly catch it. Recoverable means that it's the sort of error where the procedure could carry on, but the caller may still want to know about it. These two are suitable for general purpose libraries, depending on the case. We certainly use all three in the compiler, though primarily the first and the third. This whole scheme isn't terribly composable, though. Code must be written differently depending on whether it may abort due to errors and so forth. This is precisely what exceptions were aiming to solve, I suppose (Java's checked exceptions representing a kind of middle ground). It may be that error propagation is too important to be papered over entirely, though. Niko On 5/29/12 1:07 AM, David Rajchenbach-Teller wrote: > As promised, I have attempted to rewrite my original function |move| > with a mechanism that would let us define issue-handling policies. This > mechanism is inspired by both the current thread and the previous thread > on exceptions, a few months ago. > > So far, the result is not too convincing yet, but it can certainly serve > as a base for improvements: https://gist.github.com/2823196 > > > > _______________________________________________ > Rust-dev mailing list > Rust-dev at mozilla.org > https://mail.mozilla.org/listinfo/rust-dev From ben.striegel at gmail.com Tue May 29 08:52:07 2012 From: ben.striegel at gmail.com (Benjamin Striegel) Date: Tue, 29 May 2012 11:52:07 -0400 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FBCDC68.9020205@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> Message-ID: > * The system _must_ not prevent, some day, developers from calling Rust > from JavaScript. > * The system _must_ not prevent, some day, developers from calling > JavaScript from Rust. I'm not aware of any precedents for calling out of or into Javascript, in any language. What specific constraints does this impose? On Wed, May 23, 2012 at 8:47 AM, David Rajchenbach-Teller < dteller at mozilla.com> wrote: > Actually, one of the conclusions of our previous discussion is that > Java/C++/ML-style exceptions are probably not what we want for Rust. I > seem to remember that we also concluded that using failures as > exceptions was probably not the right course. > > Hence this new thread :) > > Let me put together what I believe are a few desirable qualities of an > "issue management system". For the moment, let's not wonder whether that > system is a language feature, a library or a coding guideline. > > * The system _must_ not prevent developers from calling C code from Rust. > * The system _must_ not prevent developers from passing a pointer to a > Rust function to C code that will call back to it. > * The system _must_ not prevent, some day, developers from calling Rust > from JavaScript. > * The system _must_ not prevent, some day, developers from calling > JavaScript from Rust. > * Issues _must_ not be restricted to integers (or to one single type). > * The default behavior in case of untreated issue _should_ be to > gracefully kill the task or the application. > * Whenever an untreated issue kills a task/application, it _should_ > produces a report usable by the developer for fixing the issue. > * It _should_ be possible to deactivate that killing behavior. There > _may_ be limitations. > * It _should_ be possible to deactivate that killing behavior > conditionally (i.e. only for some errors). > * The system _should_ eventually have a low runtime cost ? in > particular, the case in which no killing happens should be very fast. > > > Do we agree on this base? > > Cheers, > David > > On 5/22/12 4:56 AM, Bennie Kloosteman wrote: > > Are exceptions a good model for systems programming ? > > > > - legacy C programs cant call you without a wrapper which translates > > all possible exceptions > > - unwinding a stack is probably not a good idea in a kernel or when > > you transition into protected/user mode.( I know of very few kernels > > that use exceptions ). > > - Its not just legacy , Winrt uses C++ classes but returns error codes > > tor low level APIs. > > > > However its very nice for user programs . These days these different > > worlds works quite well , c libs which Is mainly used for system > > programming don't use them and C++ apps are more user programs and > > they do , C++ calls C , C rarely calls C++. Obviously if you write a > > kernel or shared library you cannot use exceptions if c programs > > call your code and there is a lot of c out there.... While not really > > an issue for the language ( just dont use exceptions) it means a > > standard lib that throws an exception would be a pain for such work > > and you would need a different standard lib , which is an issue . > > > > BTW could Rust use tasks as a substitute for exception scopes ? Tasks > > have error bubbling , hiding , "stack unwinding" , throw ( fail) and > > should have integrated logging . You could put a sugar syntax around > > it but it would still work when being called by c. Also with tasks > > you can cancel or do timeouts giving asynronous exceptions which are > > really needed ( eg in most systems cancel a long running task is very > > anoying very long pause). and which most trivial exception > > implementations don't do ..Not sure if this is the right way but there > > seems a lot of overlap and it would work with C and systems > > programming,. > > > > Ben > > _______________________________________________ > > Rust-dev mailing list > > Rust-dev at mozilla.org > > https://mail.mozilla.org/listinfo/rust-dev > > > -- > David Rajchenbach-Teller, PhD > Performance Team, Mozilla > > > _______________________________________________ > Rust-dev mailing list > Rust-dev at mozilla.org > https://mail.mozilla.org/listinfo/rust-dev > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From graydon at mozilla.com Tue May 29 18:41:54 2012 From: graydon at mozilla.com (Graydon Hoare) Date: Tue, 29 May 2012 18:41:54 -0700 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FC483B0.9010004@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> <4FC483B0.9010004@mozilla.com> Message-ID: <4FC57AE2.8080606@mozilla.com> On 5/29/2012 1:07 AM, David Rajchenbach-Teller wrote: > As promised, I have attempted to rewrite my original function |move| > with a mechanism that would let us define issue-handling policies. This > mechanism is inspired by both the current thread and the previous thread > on exceptions, a few months ago. > > So far, the result is not too convincing yet, but it can certainly serve > as a base for improvements: https://gist.github.com/2823196 Hi! Sorry I've been away, I wanted to talk about this issue, or, well, kinda just remind you and others in this conversation that there is (in my mind at least, and in the bug tracker[1]) "a plan" for how to do this. I've been continuing to assume this is something we'd handle with per-task dynamic-scoped values, just haven't got around to implementing them yet. Should be almost entirely library code. Here's a sketch of your 'move' function written in this style: https://gist.github.com/1f9c8fe1debd9a504eef I think a sufficiently gnarly implementation that's allowed to hang a special-variable list off the side of each task should be able to work given that, just keying off the different addresses of the various consts in memory. A bit of a hack but I think it might work. Note as discussed earlier, in that example the handling all happens _at_ the dynamic site of signalling (via a call to a passed-down closure, accessed through the handler library) not by unwinding. Unwinding still only happens on failure and is still idempotent, unrecoverable. Does this not sit well with you currently? -Graydon [1] Existing plan: https://github.com/mozilla/rust/issues/1945#issuecomment-4425610 https://mail.mozilla.org/pipermail/rust-dev/2011-November/000999.html https://github.com/mozilla/rust/issues/1857 From bklooste at gmail.com Tue May 29 20:13:11 2012 From: bklooste at gmail.com (Bennie Kloosteman) Date: Wed, 30 May 2012 11:13:11 +0800 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> <4FC483B0.9010004@mozilla.com> <4FC4E3F6.1080201@alum.mit.edu> Message-ID: > > In general it seems there are three classes of errors > > ? ?- globally unrecoverable (just call `fail`) > ? ?- locally unrecoverable (return a `result` type) > ? ?- recoverable (pass in handler or error reporter) > > Globally unrecoverable means that the whole task cannot continue (this is > clearly not appropriate for libraries). Since fail can be constrained to a task , its fine to have it in a library task or function designed to be used in a task... > Locally recoverable means that some > subset of the program must abort, but some outer handler could possibly > catch it. ?Recoverable means that it's the sort of error where the procedure > could carry on, but the caller may still want to know about it. ?These two > are suitable for general purpose libraries, depending on the case. ?We > certainly use all three in the compiler, though primarily the first and the > third. > > This whole scheme isn't terribly composable, though. ?Code must be written > differently depending on whether it may abort due to errors and so forth. > ?This is precisely what exceptions were aiming to solve, I suppose (Java's > checked exceptions representing a kind of middle ground). ?It may be that > error propagation is too important to be papered over entirely, though. Yep no one disputes that exceptions are better for user code that's easy to read / write but they are still not suitable for systems programming . You could have optional exceptions but not use them in corelib / systems programming/ Cinterop , can exceptions work well with regions ? Ben From arcata at gmail.com Tue May 29 20:35:29 2012 From: arcata at gmail.com (Joe Groff) Date: Tue, 29 May 2012 20:35:29 -0700 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FC4E3F6.1080201@alum.mit.edu> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> <4FC483B0.9010004@mozilla.com> <4FC4E3F6.1080201@alum.mit.edu> Message-ID: On Tue, May 29, 2012 at 7:57 AM, Niko Matsakis wrote: > In general it seems there are three classes of errors > > ? ?- globally unrecoverable (just call `fail`) > ? ?- locally unrecoverable (return a `result` type) > ? ?- recoverable (pass in handler or error reporter) If you have closures with nonlocal returns, callback handlers could work in both the locally unrecoverable and recoverable cases. Instead of returning a result type, the function could return the result of calling its handler, with the handler having the ability to jump out if a result value doesn't make sense. If a function can't handle a condition locally and wants to pass it upwards, it can take a callback of its own and call it for those cases, and that callback in turn could jump out into its own scope if necessary. --- fn foo(on_error) { let res1 = first_thing() {|| goto local_handler; }; // handle unrecoverable condition locally let res2 = second_thing() {|| on_error() }; // pass the buck let res3 = third_thing() {|| alternative_third_thing() }; // handle recoverable condition ret combination_of(res1, res2, res3); local_handler: ret alternate_thing(); } --- A somewhat more nuts-and-bolts implementation of exceptions, I guess. I know arbitrary NLRs in closures can't be handled generally without affecting performance and that break/cont in for loops are handled specially, so maybe that rules this approach out. -Joe From matthieu.monrocq at gmail.com Wed May 30 10:55:11 2012 From: matthieu.monrocq at gmail.com (Matthieu Monrocq) Date: Wed, 30 May 2012 19:55:11 +0200 Subject: [rust-dev] Back to errors, failures and exceptions In-Reply-To: <4FC57AE2.8080606@mozilla.com> References: <4FBA0B78.2080304@mozilla.com> <4FBA22B4.2070901@mozilla.com> <4FBCDC68.9020205@mozilla.com> <4FBE34B3.9060102@mozilla.com> <4FC483B0.9010004@mozilla.com> <4FC57AE2.8080606@mozilla.com> Message-ID: On Wed, May 30, 2012 at 3:41 AM, Graydon Hoare wrote: > On 5/29/2012 1:07 AM, David Rajchenbach-Teller wrote: > >> As promised, I have attempted to rewrite my original function |move| >> with a mechanism that would let us define issue-handling policies. This >> mechanism is inspired by both the current thread and the previous thread >> on exceptions, a few months ago. >> >> So far, the result is not too convincing yet, but it can certainly serve >> as a base for improvements: https://gist.github.com/**2823196 >> > > Hi! > > Sorry I've been away, I wanted to talk about this issue, or, well, kinda > just remind you and others in this conversation that there is (in my mind > at least, and in the bug tracker[1]) "a plan" for how to do this. I've been > continuing to assume this is something we'd handle with per-task > dynamic-scoped values, just haven't got around to implementing them yet. > Should be almost entirely library code. > > Here's a sketch of your 'move' function written in this style: > > https://gist.github.com/**1f9c8fe1debd9a504eef > > I think a sufficiently gnarly implementation that's allowed to hang a > special-variable list off the side of each task should be able to work > given that, just keying off the different addresses of the various consts > in memory. A bit of a hack but I think it might work. > > Note as discussed earlier, in that example the handling all happens _at_ > the dynamic site of signalling (via a call to a passed-down closure, > accessed through the handler library) not by unwinding. Unwinding still > only happens on failure and is still idempotent, unrecoverable. > > Does this not sit well with you currently? > > -Graydon > > [1] Existing plan: > https://github.com/mozilla/**rust/issues/1945#issuecomment-**4425610 > https://mail.mozilla.org/**pipermail/rust-dev/2011-**November/000999.html > https://github.com/mozilla/**rust/issues/1857 > > ______________________________**_________________ > Rust-dev mailing list > Rust-dev at mozilla.org > https://mail.mozilla.org/**listinfo/rust-dev > Hi Graydon, I must admit I really like this script. The typical issues with common errors systems are: - if you need to handle an error code, then at the error site you have precise information on the error, but no information on the context - if you need to handle an exception, then at the catch site you have information on the context but you have lost (due to unwinding) the precise information on the error By injecting closures with the required information on the context, we get the best of both worlds. I seem to recall that Lisp had a similar system (with dynamically scoped handlers), is this where this scheme takes this information from ? My main concern with Lisp was that because the handlers are dynamically scoped, it is difficult to know which handlers may be used by a particular function call or if some handlers were not set. It also requires a RAII construct to automatically reset the handler to its previous value once the current handler is no longer needed. Do you think it would be possible/desirable to instead have the user pass those handlers as arguments to the function, with null default values ? The main benefit is that it documents right in the function which cause of errors it may raise and how to handle them. The main criticism is that it ends being close to "checked exceptions", with the limitation that suddenly adding a new error-handler may require modifying all call sites if not careful (thus the idea of defaulting them) and percolate all through the callers dependencies (though it's their decision to advertise the new handler or not). Of course, the same can be said with exceptions... And it might make sense to have some specific/often required handlers be available through globals to avoid cluttering the interfaces (out_of_memory_err, divide_by_zero_err...) similar to Java's RuntimeException hierarchy which is unchecked. (Users are free to use globals or arguments, obviously) -- Matthieu -------------- next part -------------- An HTML attachment was scrubbed... URL: