Skip to content

Commit 2854ae0

Browse files
authored
Merge pull request #4680 from wilzbach/allocator_safe_2
Propagate attributes in std.experimental.allocator.make
2 parents cb09746 + ed44043 commit 2854ae0

File tree

1 file changed

+117
-5
lines changed

1 file changed

+117
-5
lines changed

std/experimental/allocator/package.d

Lines changed: 117 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -435,13 +435,39 @@ propagates the exception.
435435
auto make(T, Allocator, A...)(auto ref Allocator alloc, auto ref A args)
436436
{
437437
import std.algorithm.comparison : max;
438-
import std.conv : emplace;
438+
import std.conv : emplace, emplaceRef;
439439
auto m = alloc.allocate(max(stateSize!T, 1));
440440
if (!m.ptr) return null;
441-
scope(failure) alloc.deallocate(m);
442-
static if (is(T == class))
443-
return emplace!T(m, args);
444-
else return emplace(cast(T*) m.ptr, args);
441+
442+
// make can only be @safe if emplace or emplaceRef is `pure`
443+
auto construct()
444+
{
445+
static if (is(T == class)) return emplace!T(m, args);
446+
else
447+
{
448+
// Assume cast is safe as allocation succeeded for `stateSize!T`
449+
auto p = () @trusted { return cast(T*)m.ptr; }();
450+
emplaceRef(*p, args);
451+
return p;
452+
}
453+
}
454+
455+
scope(failure)
456+
{
457+
static if (is(typeof(() pure { return construct(); })))
458+
{
459+
// Assume deallocation is safe because:
460+
// 1) in case of failure, `m` is the only reference to this memory
461+
// 2) `m` is known to originate from `alloc`
462+
() @trusted { alloc.deallocate(m); }();
463+
}
464+
else
465+
{
466+
alloc.deallocate(m);
467+
}
468+
}
469+
470+
return construct();
445471
}
446472

447473
///
@@ -556,6 +582,92 @@ unittest
556582
test(theAllocator);
557583
}
558584

585+
// Attribute propagation
586+
nothrow @safe @nogc unittest
587+
{
588+
import std.experimental.allocator.mallocator : Mallocator;
589+
alias alloc = Mallocator.instance;
590+
591+
auto test(T, Args...)(auto ref Args args)
592+
{
593+
auto k = alloc.make!T(args);
594+
() @trusted { alloc.dispose(k); }();
595+
}
596+
597+
test!int;
598+
test!(int*);
599+
test!int(0);
600+
test!(int*)(null);
601+
}
602+
603+
// should be pure with the GCAllocator
604+
/*pure nothrow*/ @safe unittest
605+
{
606+
import std.experimental.allocator.gc_allocator : GCAllocator;
607+
608+
alias alloc = GCAllocator.instance;
609+
610+
auto test(T, Args...)(auto ref Args args)
611+
{
612+
auto k = alloc.make!T(args);
613+
(a) @trusted { a.dispose(k); }(alloc);
614+
}
615+
616+
test!int();
617+
test!(int*);
618+
test!int(0);
619+
test!(int*)(null);
620+
}
621+
622+
// Verify that making an object by calling an impure constructor is not @safe
623+
nothrow @safe @nogc unittest
624+
{
625+
import std.experimental.allocator.mallocator : Mallocator;
626+
static struct Pure { this(int) pure nothrow @nogc @safe {} }
627+
628+
cast(void) Mallocator.instance.make!Pure(0);
629+
630+
static int g = 0;
631+
static struct Impure { this(int) nothrow @nogc @safe {
632+
g++;
633+
} }
634+
static assert(!__traits(compiles, cast(void) Mallocator.instance.make!Impure(0)));
635+
}
636+
637+
// test failure with a pure, failing struct
638+
@safe unittest
639+
{
640+
import std.exception : assertThrown, enforce;
641+
642+
// this struct can't be initialized
643+
struct InvalidStruct
644+
{
645+
this(int b)
646+
{
647+
enforce(1 == 2);
648+
}
649+
}
650+
import std.experimental.allocator.mallocator : Mallocator;
651+
assertThrown(make!InvalidStruct(Mallocator.instance, 42));
652+
}
653+
654+
// test failure with an impure, failing struct
655+
@system unittest
656+
{
657+
import std.exception : assertThrown, enforce;
658+
static int g;
659+
struct InvalidImpureStruct
660+
{
661+
this(int b)
662+
{
663+
g++;
664+
enforce(1 == 2);
665+
}
666+
}
667+
import std.experimental.allocator.mallocator : Mallocator;
668+
assertThrown(make!InvalidImpureStruct(Mallocator.instance, 42));
669+
}
670+
559671
private void fillWithMemcpy(T)(void[] array, auto ref T filler) nothrow
560672
{
561673
import core.stdc.string : memcpy;

0 commit comments

Comments
 (0)