digitalmars.D.learn - Placement new and trusted
- IchorDev (19/19) Sep 10 I thought the 'placement new' feature might replace my need for
- Richard (Rikki) Andrew Cattermole (3/24) Sep 10 Your calling a @system function (constructor) from a safe function (main...
- IchorDev (9/12) Sep 10 You don't understand: I *WANT* the code *FAIL* to compile because
- Richard (Rikki) Andrew Cattermole (5/5) Sep 10 Ok, gotcha.
- IchorDev (8/12) Sep 10 Pretty much.
- Richard (Rikki) Andrew Cattermole (6/15) Sep 10 It does replace emplace, and just like emplace you can double-init
- IchorDev (7/9) Sep 10 Yeah but I should be able to mark it as `@trusted` without
- Richard (Rikki) Andrew Cattermole (4/13) Sep 10 It did go through the DIP process.
- IchorDev (5/6) Sep 11 Blast, you're right. I've even seen the DIP Development post for
- Paul Backus (10/22) Sep 10 The problem is not really with placement new, it's with
- IchorDev (15/21) Sep 11 But that's the thing: all I want is to construct objects into
- Nick Treleaven (13/27) Sep 12 No, for 2 reasons:
- Dennis (18/19) Sep 11 The trick that's used in druntime is putting the part that still
- Paul Backus (15/21) Sep 11 You can also use `static if` and a no-op `@system` function:
- IchorDev (3/11) Sep 12 Unfortunately that would give you an incredibly unhelpful error
- IchorDev (18/32) Sep 12 Well, that would work. Thank you for pointing it out. It's
I thought the 'placement new' feature might replace my need for
`emplace`, but I don't see a way to mark a placement new as
` trusted` without marking the call to the object's constructor
as ` trusted` also?
```d
void main() safe{
X x;
//placement new is system, so we have to wrap it in trusted:
X* xPtr = (() trusted => new (x) X(500))();
}
struct X{
void* a;
this(int a) system{
//but we don't want to mark THIS as trusted!
this.a = cast(void*)a;
}
}
```
If anyone has any ideas, please let me know.
Sep 10
On 11/09/2025 12:29 AM, IchorDev wrote:
I thought the 'placement new' feature might replace my need for
`emplace`, but I don't see a way to mark a placement new as ` trusted`
without marking the call to the object's constructor as ` trusted` also?
```d
void main() safe{
X x;
//placement new is system, so we have to wrap it in trusted:
X* xPtr = (() trusted => new (x) X(500))();
}
struct X{
void* a;
this(int a) system{
//but we don't want to mark THIS as trusted!
this.a = cast(void*)a;
}
}
```
If anyone has any ideas, please let me know.
Your calling a system function (constructor) from a safe function (main).
Placement new doesn't change the rules around this.
Sep 10
On Wednesday, 10 September 2025 at 12:41:22 UTC, Richard (Rikki) Andrew Cattermole wrote:Your calling a system function (constructor) from a safe function (main). Placement new doesn't change the rules around this.You don't understand: I *WANT* the code *FAIL* to compile because `S`'s constructor is ` system`. But instead, the ` system` constructor gets through because I had to use ` trusted` to get placement new itself to be considered safe. If I have to manually check constructor safety to get around this then I might as well just use `emplace`, which would make this a completely worthless feature.
Sep 10
Ok, gotcha. Placement new is system, but you wanted to use it in an safe function iff the constructor to be called is safe as well. The reason placement new is system is because of double-init. It can't be a safe operation.
Sep 10
On Wednesday, 10 September 2025 at 13:13:34 UTC, Richard (Rikki) Andrew Cattermole wrote:Placement new is system, but you wanted to use it in an safe function iff the constructor to be called is safe as well.Pretty much.The reason placement new is system is because of double-init. It can't be a safe operation.What?! Like, it assigns to the memory twice? What's the point of this feature, then? I thought this could essentially replace `emplace` for constructing types into uninitialised memory, but instead it's just a booby-trap that stops people from making their code ` safe`?
Sep 10
On 11/09/2025 2:31 AM, IchorDev wrote:On Wednesday, 10 September 2025 at 13:13:34 UTC, Richard (Rikki) Andrew Cattermole wrote:It does replace emplace, and just like emplace you can double-init memory with it. There is no protection against it apart from it being system (which emplace should also be). The caller is responsible for guaranteeing that the memory passed in is uninitialized, and that behavior is system.The reason placement new is system is because of double-init. It can't be a safe operation.What?! Like, it assigns to the memory twice? What's the point of this feature, then? I thought this could essentially replace `emplace` for constructing types into uninitialised memory, but instead it's just a booby-trap that stops people from making their code ` safe`?
Sep 10
On Wednesday, 10 September 2025 at 14:45:52 UTC, Richard (Rikki) Andrew Cattermole wrote:The caller is responsible for guaranteeing that the memory passed in is uninitialized, and that behavior is system.Yeah but I should be able to mark it as ` trusted` without trusting some random bloody constructor as well. Apparently nobody thought to use a syntax that allows marking one but not the other? This is why we need to use the DIP process to add new features like this.
Sep 10
On 11/09/2025 3:27 AM, IchorDev wrote:On Wednesday, 10 September 2025 at 14:45:52 UTC, Richard (Rikki) Andrew Cattermole wrote:It did go through the DIP process. What has not gone through the DIP process is attributes like localtrusted which would be more akin to what you're asking about.The caller is responsible for guaranteeing that the memory passed in is uninitialized, and that behavior is system.Yeah but I should be able to mark it as ` trusted` without trusting some random bloody constructor as well. Apparently nobody thought to use a syntax that allows marking one but not the other? This is why we need to use the DIP process to add new features like this.
Sep 10
On Thursday, 11 September 2025 at 03:47:02 UTC, Richard (Rikki) Andrew Cattermole wrote:It did go through the DIP process.Blast, you're right. I've even seen the DIP Development post for it before, too! I didn't read into it at the time. I could've probably identified this problem and raised it ahead of time.
Sep 11
On Wednesday, 10 September 2025 at 14:31:27 UTC, IchorDev wrote:On Wednesday, 10 September 2025 at 13:13:34 UTC, Richard (Rikki) Andrew Cattermole wrote:The problem is not really with placement new, it's with constructors. Constructors are allowed to mutate immutable objects (under the assumption that they are initializing a newly-created object). If you call a constructor twice on the same immutable object, which is possible with placement new, it can result in undefined behavior. In fact, it is also possible to trigger UB this way by simply calling the constructor manually: https://github.com/dlang/dmd/issues/20248Placement new is system, but you wanted to use it in an safe function iff the constructor to be called is safe as well.Pretty much.The reason placement new is system is because of double-init. It can't be a safe operation.What?! Like, it assigns to the memory twice? What's the point of this feature, then? I thought this could essentially replace `emplace` for constructing types into uninitialised memory, but instead it's just a booby-trap that stops people from making their code ` safe`?
Sep 10
On Thursday, 11 September 2025 at 04:19:02 UTC, Paul Backus wrote:The problem is not really with placement new, it's with constructors. Constructors are allowed to mutate immutable objects (under the assumption that they are initializing a newly-created object). If you call a constructor twice on the same immutable object, which is possible with placement new, it can result in undefined behavior.But that's the thing: all I want is to construct objects into *freshly-allocated*, *uninitialised memory*; so my desired use-case has a safe interface and can therefore be marked ` trusted`. However the constructor is a wildcard, so I want to leave that part to attribute inference. Do you think it'd be worth submitting an enhancement issue to add something simple like this? ```d new trusted (buffer) S(500); //we trust that the buffer is safe to use, but not S's constructor. ``` The weird placement new syntax means that this looks a bit goofy, but it's better than the feature being essentially dead-on-arrival for the *one thing* I'd ever want it for.
Sep 11
On Thursday, 11 September 2025 at 08:06:17 UTC, IchorDev wrote:But that's the thing: all I want is to construct objects into *freshly-allocated*, *uninitialised memory*; so my desired use-case has a safe interface and can therefore be marked ` trusted`. However the constructor is a wildcard, so I want to leave that part to attribute inference. Do you think it'd be worth submitting an enhancement issue to add something simple like this? ```d new trusted (buffer) S(500); //we trust that the buffer is safe to use, but not S's constructor. ```No, for 2 reasons: 1. ` trusted` is a function attribute that says the function has a safe interface. Placement new does not have a safe interface, above it depends on how `buffer` is used outside of it and what `S` is. (Yes people often resort to using a trusted function literal without a safe interface, but it's still wrong, and the language shouldn't endorse that). 2. There are various other expressions in the language where you'd need something similar. Phobos uses introspection instead to detect whether to trust an expression or not.The weird placement new syntax means that this looks a bit goofy, but it's better than the feature being essentially dead-on-arrival for the *one thing* I'd ever want it for.It's no different than some other unsafe expressions that Phobos sometimes needs to trust.
Sep 12
On Wednesday, 10 September 2025 at 12:29:24 UTC, IchorDev wrote:If anyone has any ideas, please let me know.The trick that's used in druntime is putting the part that still needs to be checked for attributes inside an `if (false)` block, for example: https://github.com/dlang/dmd/blob/c81714b9bc7626e1d235fb2d6279dc73d47c9174/druntime/src/core/lifetime.d#L1981 ```D private T moveImpl(T)(return scope ref T source) { // Properly infer safety from moveEmplaceImpl as the implementation below // might void-initialize pointers in result and hence needs to be trusted if (false) moveEmplaceImpl(source, source); return trustedMoveImpl(source); } ``` Other example: https://github.com/dlang/dmd/blob/c81714b9bc7626e1d235fb2d6279dc73d47c9174/druntime/src/core/internal/newaa.d#L692
Sep 11
On Thursday, 11 September 2025 at 11:47:40 UTC, Dennis wrote:On Wednesday, 10 September 2025 at 12:29:24 UTC, IchorDev wrote:You can also use `static if` and a no-op ` system` function: ```d system pure nothrow nogc pragma(inline, true) void inferSystem() {} T* example(T)() { import std.traits: isSafe; static if (!isSafe!(() { new T(); }) inferSystem(); void[] memory = new void[](T.sizeof); return (() trusted => new (memory) T())(); } ```If anyone has any ideas, please let me know.The trick that's used in druntime is putting the part that still needs to be checked for attributes inside an `if (false)` block, for example: https://github.com/dlang/dmd/blob/c81714b9bc7626e1d235fb2d6279dc73d47c9174/druntime/src/core/lifetime.d#L1981
Sep 11
On Thursday, 11 September 2025 at 17:16:35 UTC, Paul Backus wrote:
```d
void inferSystem() system pure nothrow nogc {}
T* example(T)() {
static if (!isSafe!(() { new T(); })
inferSystem();
return (() trusted => new (new void[](T.sizeof)) T())();
}
```
Unfortunately that would give you an incredibly unhelpful error
message.
Sep 12
On Thursday, 11 September 2025 at 11:47:40 UTC, Dennis wrote:
The trick that's used in druntime is putting the part that
still needs to be checked for attributes inside an `if (false)`
block, for example:
```d
private T moveImpl(T)(return scope ref T source)
{
// Properly infer safety from moveEmplaceImpl as the
implementation below
// might void-initialize pointers in result and hence needs
to be trusted
if (false) moveEmplaceImpl(source, source);
return trustedMoveImpl(source);
}
```
Well, that would work. Thank you for pointing it out. It's
probably better to do it like this to avoid the potential for an
unconditional cache miss (depending on how smart the compiler
feels today):
```d
private T moveImpl(T)(return scope ref T source){
if(true) return trustedMoveImpl(source);
moveEmplaceImpl(source, source);
assert(0);
}
```
It would still be nice to have a proper, intuitive way of doing
this though. Hacks are great and all, but they are still hacks.
One time I got my code to call into Swift code by writing inline
assembly to match Swift's calling convention; but that was a
hack, and I'm still excited that LDC has recently gained some
degree of native Swift interoperability.
Sep 12









IchorDev <zxinsworld gmail.com> 