www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Contract error string generation is not implicitly nogc

reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
void foo(string s)  safe pure nothrow  nogc
in (s != "hello", "Invalid " ~ s) {
}

void main() {}

Error: cannot use operator `~` in ` nogc` function `deneme.foo`

But that operator is used only when we are about to throw an Error, 
which is supposed to imply we shouldn't continue with the program 
anyway. So, just one little GC allocation wouldn't hurt, right? :)

Is there any downside to allowing GC allocation in that case?

Otherwise, we have to throw away either  nogc annotation or the 
functionality of providing useful error messages.

Ali
Sep 04 2022
next sibling parent reply IGotD- <nise nise.com> writes:
On Sunday, 4 September 2022 at 14:06:44 UTC, Ali Çehreli wrote:
 void foo(string s)  safe pure nothrow  nogc
 in (s != "hello", "Invalid " ~ s) {
 }

 void main() {}

 Error: cannot use operator `~` in ` nogc` function `deneme.foo`

 But that operator is used only when we are about to throw an 
 Error, which is supposed to imply we shouldn't continue with 
 the program anyway. So, just one little GC allocation wouldn't 
 hurt, right? :)
...but what if the GC is totally disabled or not even initialized? This might be the environment you'd want to use nogc.
Sep 04 2022
parent reply Paul Backus <snarwin gmail.com> writes:
On Sunday, 4 September 2022 at 14:30:51 UTC, IGotD- wrote:
 On Sunday, 4 September 2022 at 14:06:44 UTC, Ali Çehreli wrote:
 void foo(string s)  safe pure nothrow  nogc
 in (s != "hello", "Invalid " ~ s) {
 }

 void main() {}

 Error: cannot use operator `~` in ` nogc` function `deneme.foo`

 But that operator is used only when we are about to throw an 
 Error, which is supposed to imply we shouldn't continue with 
 the program anyway. So, just one little GC allocation wouldn't 
 hurt, right? :)
...but what if the GC is totally disabled or not even initialized? This might be the environment you'd want to use nogc.
If the GC is totally disabled (like in BetterC), the expression `"Invalid " ~ s` will not even compile in the first place. So you would need to create your error message some other way (perhaps with malloc).
Sep 04 2022
parent reply IGotD- <nise nise.com> writes:
On Sunday, 4 September 2022 at 14:54:52 UTC, Paul Backus wrote:
 If the GC is totally disabled (like in BetterC), the expression 
 `"Invalid " ~ s` will not even compile in the first place. So 
 you would need to create your error message some other way 
 (perhaps with malloc).
Which make me wonder if the nogc attribute is totally unnecessary. What if we are using reference counting which only needs malloc/free, then it might be acceptable. Wouldn't it be better if we get an error like GCBohem.Allocate symbol isn't found, the reason would be obvious. Then you might ban GC from specific functions which perhaps is you want. However, suddenly we have fringe desires like in this thread.
Sep 04 2022
parent tsbockman <thomas.bockman gmail.com> writes:
On Sunday, 4 September 2022 at 15:05:45 UTC, IGotD- wrote:
 Which make me wonder if the  nogc attribute is totally 
 unnecessary.
 ...
 Wouldn't it be better if we get an error like GCBohem.Allocate 
 symbol isn't found, the reason would be obvious.
One of the main motivators for avoiding the GC is to prevent low latency code from suffering the GC's "stop the world" pauses. By operating at the function level, rather than the whole program level, ` nogc` can help write correct programs where latency tolerant code can use the GC, and low latency code can avoid it by running in separate threads that don't get stopped durring collections. Simply removing the GC entirely from a program forces everything to be ` nogc`, instead of just the most latency sensitive parts. (Having said that, ` nogc` is a very incomplete and unsafe solution to this problem, since it doesn't make any attempt to prevent GC-using parts of the program from passing ownership of GC memory to ` nogc` threads through parameters or shared memory, nor does it prevent the use of modules with GC dependant module thread-local constructors.)
Sep 04 2022
prev sibling next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
First of all, I messed up the subject line. I didn't mean "implicit 
 nogc" but " nogc should still allow allocation" for contract strings.

On 9/4/22 07:06, Ali Çehreli wrote:

 we are about to throw an Error,
 which is supposed to imply we shouldn't continue with the program
 anyway.
I went ahead and over-engineered a solution which is probably broken in some important way but I am happy with it. :) The trick is, instead of making an error string right there and then, an Error is thrown, which contains some data. The GC is used later when error data is being used. Good luck if data has a copy constructor which needs the GC. :p This is a best-effort mechanism because there is a pre-thread instance for that specific Error, which carries error data. Here is how it works: The programmer defines an Error with a distinguishing string tag. The SumType definition must list all kinds of data that such an error can carry. (Luckily, a compile-time error instructs the programmer about the missing Tuple types that need to be added.) mixin NoGcError!("Foo", SumType!(Tuple!(Foo, int, int), Tuple!(string), Tuple!())); Although "Foo" could be anything, it makes sense to associate it e.g. with a struct: struct Foo { int i; The user must specify "Foo" when producing the error string: void bar(int i, int j) safe pure nothrow nogc in (i == 42, error!"Foo"("Something is not right", this, i, j)) { // ... } Note different type of error data here: void zar(string str, double d) safe pure nothrow nogc in (!isNaN(d), error!"Foo"("Must not be nan", str)) { // ... } } Of course, error!"Foo" can be used to throw outside of contracts as well: void main() { auto f = Foo(1); // f.bar(42, 44); // f.zar("hello world", double.init); error!"Foo"("Wroing!"); } Here is a complete draft: ```D import std; // Sorry :( mixin NoGcError!("Foo", SumType!(Tuple!(Foo, int, int), Tuple!(string), Tuple!())); struct Foo { int i; void bar(int i, int j) safe pure nothrow nogc in (i == 42, error!"Foo"("Something is not right", this, i, j)) { // ... } void zar(string str, double d) safe pure nothrow nogc in (!isNaN(d), error!"Foo"("Must not be nan", str)) { // ... } } void main() { auto f = Foo(1); // f.bar(42, 44); // f.zar("hello world", double.init); error!"Foo"("Wroing!"); } mixin template NoGcError(string tag, Data) { class NoGcError : Error { string msg; Data data; enum noDataString = Tuple!()().to!string; this() { super(tag ~ " Error"); } // Adapted from object.Throwable.toString override void toString(scope void delegate(in char[]) sink) const { sink(file); sink(":"); sink(line.to!string); sink(": "); sink(tag); sink(" Error: "); sink(msg); const dataStr = data.to!string; if (dataStr != noDataString) { sink("\n Data: "); sink(dataStr); } if (info) { try { sink("\n----------------"); foreach (t; info) { sink("\n"); sink(t); } } catch (Throwable) { // ignore more errors } } } } static ref error_(string t)() if (t == tag) { static NoGcError err_; return err_; } static this() { error_!tag = new NoGcError(); } } string error(string tag, Data...)(string msg, Data data, string file = __FILE__, size_t line = __LINE__) safe pure nothrow nogc { static auto thrower(string msg, Data data, string file, size_t line) trusted nothrow nogc { static assert (__traits(compiles, error_!tag.data = tuple(data)), format!`SumType of NoGcError!"%s" must include Tuple!%s`(tag, Data.stringof)); error_!tag.msg = msg; error_!tag.data = tuple(data); error_!tag.file = file; error_!tag.line = line; throw error_!tag; } // Adapted from std/regex/internal/ir.d static assumePureFunction(T)(T t) trusted pure nothrow nogc { enum attrs = functionAttributes!T | FunctionAttribute.pure_; return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; } assumePureFunction(&thrower)(msg, data, file, line); assert(false); } ``` Ali
Sep 05 2022
prev sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 9/4/22 07:06, Ali Çehreli wrote:
 void foo(string s)  safe pure nothrow  nogc
 in (s != "hello", "Invalid " ~ s) {
 }

 void main() {}

 Error: cannot use operator `~` in ` nogc` function `deneme.foo`
I have an experimental solution that can throw Error objects from nogc code: https://code.dlang.org/packages/alid%3Aerrornogc Documentation: https://alid.dpldocs.info/v0.1.1/alid.errornogc.NogcError.html Ali
Sep 12 2022