@@ -435,13 +435,39 @@ propagates the exception.
435435auto 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+
559671private void fillWithMemcpy (T)(void [] array, auto ref T filler) nothrow
560672{
561673 import core.stdc.string : memcpy;
0 commit comments