-
-
Notifications
You must be signed in to change notification settings - Fork 416
Make malloc and calloc @trusted #1183
Conversation
They are indeed those but it's impossible to use them in any safe way because they return a |
They can be safely passed around to other functions that may or may not do unsafe things. Whether they are What do you think about pure though? It might not seem like a big deal at this level of abstraction, but it makes things like constructing The kinds of memory allocation that are currently considered pure are GC-heap allocations and stack allocations (although I don't know how we've annotated |
These absolutely cannot be They can be pure, and I would argue they should be. However, free would also have to be pure to make pure malloc viable, and that poses some problems. I hashed this out with someone in the NG one time and the short of it is, So while I think malloc and friends could be pure, because free cannot be, it's not something we should support. A wrapper around it that auto-frees (possibly ref counting) may be something that makes sense, but I'd have to see it to know for sure. |
yah, pure is nice! |
👍 for pure.
No, it can't be strong pure, you can only call free with a pointer to mutable. |
What's up with the test failure? |
This reminds me of the fact, that the IAllocator interface (std.allocator) should also have a strongly pure Chandler Carruth was talking about this problem at the last CppCon. |
My concern isn't calling free with an immutable, but calling free from inside a pure function that has an immutable: void freeIt(immutable(int)* x) pure
{
free(cast(void *)x);
} Basically, if you can't free immutable malloc'd data in pure functions, then you can't create immutable malloc'd data with pure functions. I don't think that cast requirement will stop people. |
Indeed, looks troublesome. Especially because memory allocated by strong pure functions is implicitly convertible to immutable (because of the unique ownership). |
Let's close this until better arguments come up. |
They have a safe interface. They absolutely can.
|
This comes up from time to time, the main problem to solve is that a pure |
So if we also make free weakly pure, than this would be useful. The only catch is that whomever uses it, needs to be aware of the fact that freeing immutable data might ellide the call. Sounds like it's worth a try, b/c the problem can be handled by lifetime wrappers such as Unique or RefCounted. |
Ping @JakobOvrum, can you extend that to free please? |
197dc6c
to
b879ab5
Compare
Made
You don't have to allocate and deallocate in the same function. As for that cast, |
If the compiler can treat D pure in the C sense, I suspect you need more guarantee than strong pure (at least not without false positives). |
Not sure why this fails but anyhow - anything else left for this ? |
I totally agree on this AFAICT making Seems like DMD purity optimizations on multiple successive calls to
AFAICT, we need some function qualifier, say @Allocator, to indicate that a function is a memory allocator. Such a function may be called by Any ideas, @WalterBright ? |
|
I agree with @WalterBright. The appropriate place for these sort of guarantees is |
@ZombineDev interesting. Does adding "attributes in a private extern declaration" work today? If so, I see no reason why AFAICT, its Update: I tried replacing import core.stdc.stdlib : malloc; with extern(C) private void* malloc(size_t size) shared pure @system nothrow @nogc; in
What's wrong with my extern declaration? Update: Ahh. I solved it. I had to put the extern declaration at the module level and not inside the definition of Should I create a PR for mallocator.d and alikes? |
You can make a pure allocator like this:
and then in the import:
which works because C mangling is not affected by the attributes. No compiler extensions are needed. |
@WalterBright is this pattern preferred over a module private import like extern(C) void* malloc(size_t size) shared pure @system nothrow @nogc; ? |
@nordlow I think that your previous linker error was becuase you defined a shared function pointer variable named
Removing shared should solve the linking problem you mentioned. |
The OOM-checked code posted here was a part of dlang/phobos/pull/3031, which served as much of the motivation for filing this PR. I agree that the null return value is an unacceptable source of impurity. I'll remove Note that std.allocator also returns null on OOM, both |
So... shall we close this? |
What if we somehow magically mark malloc and friends as weak-pure (regardless of what signature tells) in the compiler? That should allow us to use malloc/free in pure functions w/o the side effect of optimizing them away. |
@DmitryOlshansky What about a function that accepts an immutable pointer that it frees? How do we stop it from eliding that call if free is weak pure? See my example above. If we are going to treat free specially, I'd like to see a general mechanism for this instead of magic compilers. Even if it's undocumented/obscure. I still don't think it's a good idea... |
Weak pure calls cannot be elided in general, so no point in trying, not even in pure functions. |
Adding special uda "WeakPure" should do the trick, meaning that compiler shouldn't attempt optimizations based on purity (in other words treat as impure except that it's usable in pure functions) |
Right, but strong pure functions that call weak pure functions can. This was my point here: #1183 (comment) |
Okay... Just add |
Or... we just cheat with extern(C) for the small cases (e.g. Ref counting) where it is safe. Marking free pure opens any users up to a huge number of problems. |
I'm going to close this. There are reasonable ways outlined here to use malloc/free in pure code. There is no need to make them pure in the general case, which would make |
malloc
andcalloc
do not present an unsafe interface - it's reasonable to call them and do the unsafe cast or pointer slice somewhere else. Hence, they should be@trusted
.However, I made this PR mostly to gauge opinion about purity. I suggest these two simple heap allocators should be pure in the same vein
new
andGC.malloc
are considered pure - memory allocation is only an observable side-effect in really niche applications that either a) inspect memory usage using OS-specific functions, or b) recover from OOM. In all other applications it is immensely practical to allow memory allocation in pure functions.What do you think?