June 08, 2017
On Thursday, 8 June 2017 at 13:02:38 UTC, ag0aep6g wrote:
> Catching the resulting error is @safe when you throw the int* away. So if f is `pure` and you make sure that the arguments don't survive the `try` block, you're good, because f supposedly cannot have reached anything else. This is your proposal, right?

Right.

> I don't think that's sound. At least, it clashes with another relatively recent development:
>
> https://dlang.org/phobos/core_memory.html#.pureMalloc
>
> That's a wrapper around C's malloc. C's malloc might set the global errno, so it's impure. pureMalloc achieves purity by resetting errno to the value it had before the call.
>
> So a `pure` function may mess with global state, as long as it cleans it up. But when it's interrupted (e.g. by an out-of-bounds error), it may leave globals in an invalid state. So you can't assume that a `pure` function upholds its purity when it throws an error.

That's true. A "pure after cleanup" function is incompatible with catching Errors (unless we introduce a "scope(error)" keyword that also runs on errors, but that comes with other problems).

Is pureMalloc supposed to be representative of pure functions, or more of a special case? That's not a rhetorical question, I genuinely don't know.

The spec says a pure function "does not read or write any global or static mutable state", which seems incompatible with "save a global, then write it back like it was". In fact, doing so seems contrary to the assumption that you can run any two pure functions on immutable / independent data at the same time and you won't have race conditions.

Actually, now I'm wondering whether pureMalloc & co handle potential race conditions at all, or just hope they don't happen.
June 08, 2017
On 6/8/17 9:42 AM, Olivier FAURE wrote:
> On Thursday, 8 June 2017 at 12:20:19 UTC, Steven Schveighoffer wrote:
>> Hm... if you locked an object that was passed in on the stack, for
>> instance, there is no guarantee the object gets unlocked.
>>
>
> This wouldn't be allowed unless the object was duplicated / created
> inside the try block.

void foo(Mutex m, Data d) pure
{
   synchronized(m)
   {
   	// ... manipulate d
   } // no guarantee m gets unlocked
}

-Steve
June 08, 2017
On 06/08/2017 04:02 PM, Olivier FAURE wrote:
> That's true. A "pure after cleanup" function is incompatible with catching Errors (unless we introduce a "scope(error)" keyword that also runs on errors, but that comes with other problems).
> 
> Is pureMalloc supposed to be representative of pure functions, or more of a special case? That's not a rhetorical question, I genuinely don't know.

I think it's supposed to be just as pure as any other pure function.

Here's the pull request that added it:
https://github.com/dlang/druntime/pull/1746

I don't see anything about it being special-cased in the compiler or such.

> The spec says a pure function "does not read or write any global or static mutable state", which seems incompatible with "save a global, then write it back like it was".

True.

Something similar is going on with @safe. There's a list of things that are "not allowed in safe functions" [1], but you can do all those things in @trusted code, of course. The list is about what the compiler rejects, not about what a @safe function can actually do. It might be the same with the things that pure functions can/cannot do.

I suppose the idea is that it cannot be observed that pureMalloc messes with global state, so it's ok. The assumption being that you don't catch errors.

By the way, with regards to purity and errors, `new` is the same as pureMalloc. When `new` throws an OutOfMemoryError and you catch it, you can see that errno has been set. Yet `new` is considered `pure`.

> In fact, doing so seems contrary to the assumption that you can run any two pure functions on immutable / independent data at the same time and you won't have race conditions.
> 
> Actually, now I'm wondering whether pureMalloc & co handle potential race conditions at all, or just hope they don't happen.

Apparently errno is thread-local.



[1] https://dlang.org/spec/function.html#safe-functions
June 08, 2017
On Thursday, 8 June 2017 at 14:13:53 UTC, Steven Schveighoffer wrote:

> void foo(Mutex m, Data d) pure
> {
>    synchronized(m)
>    {
>    	// ... manipulate d
>    } // no guarantee m gets unlocked
> }
>
> -Steve

Isn't synchronized(m) not nothrow?
June 08, 2017
On 6/8/17 11:19 AM, Stanislav Blinov wrote:
> On Thursday, 8 June 2017 at 14:13:53 UTC, Steven Schveighoffer wrote:
>
>> void foo(Mutex m, Data d) pure
>> {
>>    synchronized(m)
>>    {
>>        // ... manipulate d
>>    } // no guarantee m gets unlocked
>> }
>>
>
> Isn't synchronized(m) not nothrow?

You're right, it isn't. I actually didn't know that. Also forgot to make my function nothrow. Fixed:

void foo(Mutex m, Data d) pure nothrow
{
   try
   {
      synchronized(m)
      {
         // .. manipulate d
      }
   }
   catch(Exception)
   {
   }
}

-Steve
June 08, 2017
I want to start by stating that the discussion around being able to throw Error from nothrow functions and the compiler optimizations that follow is important to the thoughts below.

The other aspect of array bounds checking is that those particular checks will not be added in -release. There has been much discussion around this already and I do recall that the solution was that @safe code will retain the array bounds checks (I'm not sure if contracts was included in this). Thus if using -release and @safe you'd be able to rely on having an Error to catch.

Now it might make sense for @safe code to throw an ArrayOutOfBounds Exception, but that would mean the function couldn't be marked as nothrow if array indexing is used. This is probably a terrible idea, but @safe nothrow functions could throw ArrayIndexError while @safe could throw ArrayIndexException. It would really suck that adding nothrow would change the semantics silently.
1 2 3
Next ›   Last »