aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/array.d
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2019-06-18 20:42:10 +0200
committerIain Buclaw <ibuclaw@gdcproject.org>2021-11-30 16:53:28 +0100
commit5fee5ec362f7a243f459e6378fd49dfc89dc9fb5 (patch)
tree61d1bdbca854a903c0860406f457f06b2040be7a /libphobos/src/std/array.d
parentb3f60112edcb85b459e60f66c44a55138b1cef49 (diff)
downloadgcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.zip
gcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.tar.gz
gcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.tar.bz2
d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)
The D front-end is now itself written in D, in order to build GDC, you will need a working GDC compiler (GCC version 9.1 or later). GCC changes: - Add support for bootstrapping the D front-end. These add the required components in order to have a D front-end written in D itself. Because the compiler front-end only depends on the core runtime modules, only libdruntime is built for the bootstrap stages. D front-end changes: - Import dmd v2.098.0-beta.1. Druntime changes: - Import druntime v2.098.0-beta.1. Phobos changes: - Import phobos v2.098.0-beta.1. The jump from v2.076.1 to v2.098.0 covers nearly 4 years worth of development on the D programming language and run-time libraries. ChangeLog: * Makefile.def: Add bootstrap to libbacktrace, libphobos, zlib, and libatomic. * Makefile.in: Regenerate. * Makefile.tpl (POSTSTAGE1_HOST_EXPORTS): Fix command for GDC. (STAGE1_CONFIGURE_FLAGS): Add --with-libphobos-druntime-only if target-libphobos-bootstrap. (STAGE2_CONFIGURE_FLAGS): Likewise. * configure: Regenerate. * configure.ac: Add support for bootstrapping D front-end. config/ChangeLog: * acx.m4 (ACX_PROG_GDC): New m4 function. gcc/ChangeLog: * Makefile.in (GDC): New variable. (GDCFLAGS): New variable. * configure: Regenerate. * configure.ac: Add call to ACX_PROG_GDC. Substitute GDCFLAGS. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd b8384668f. * Make-lang.in (d-warn): Use strict warnings. (DMD_WARN_CXXFLAGS): Remove. (DMD_COMPILE): Remove. (CHECKING_DFLAGS): Define. (WARN_DFLAGS): Define. (ALL_DFLAGS): Define. (DCOMPILE.base): Define. (DCOMPILE): Define. (DPOSTCOMPILE): Define. (DLINKER): Define. (DLLINKER): Define. (D_FRONTEND_OBJS): Add new dmd front-end objects. (D_GENERATED_SRCS): Remove. (D_GENERATED_OBJS): Remove. (D_ALL_OBJS): Remove D_GENERATED_OBJS. (d21$(exeext)): Build using DLLINKER and -static-libphobos. (d.tags): Remove dmd/*.c and dmd/root/*.c. (d.mostlyclean): Remove D_GENERATED_SRCS, d/idgen$(build_exeext), d/impcnvgen$(build_exeext). (D_INCLUDES): Include $(srcdir)/d/dmd/res. (CFLAGS-d/id.o): Remove. (CFLAGS-d/impcnvtab.o): Remove. (d/%.o): Build using DCOMPILE and DPOSTCOMPILE. Update dependencies from d/dmd/%.c to d/dmd/%.d. (d/idgen$(build_exeext)): Remove. (d/impcnvgen$(build_exeext)): Remove. (d/id.c): Remove. (d/id.h): Remove. (d/impcnvtab.c): Remove. (d/%.dmdgen.o): Remove. (D_SYSTEM_H): Remove. (d/idgen.dmdgen.o): Remove. (d/impcnvgen.dmdgen.o): Remove. * config-lang.in (boot_language): New variable. * d-attribs.cc: Include dmd/expression.h. * d-builtins.cc: Include d-frontend.h. (build_frontend_type): Update for new front-end interface. (d_eval_constant_expression): Likewise. (d_build_builtins_module): Likewise. (maybe_set_builtin_1): Likewise. (d_build_d_type_nodes): Likewise. * d-codegen.cc (d_decl_context): Likewise. (declaration_reference_p): Likewise. (declaration_type): Likewise. (parameter_reference_p): Likewise. (parameter_type): Likewise. (get_array_length): Likewise. (build_delegate_cst): Likewise. (build_typeof_null_value): Likewise. (identity_compare_p): Likewise. (lower_struct_comparison): Likewise. (build_filename_from_loc): Likewise. (build_assert_call): Remove LIBCALL_SWITCH_ERROR. (build_bounds_index_condition): Call LIBCALL_ARRAYBOUNDS_INDEXP on bounds error. (build_bounds_slice_condition): Call LIBCALL_ARRAYBOUNDS_SLICEP on bounds error. (array_bounds_check): Update for new front-end interface. (checkaction_trap_p): Handle CHECKACTION_context. (get_function_type): Update for new front-end interface. (d_build_call): Likewise. * d-compiler.cc: Remove include of dmd/scope.h. (Compiler::genCmain): Remove. (Compiler::paintAsType): Update for new front-end interface. (Compiler::onParseModule): Likewise. * d-convert.cc (convert_expr): Remove call to LIBCALL_ARRAYCAST. (convert_for_rvalue): Update for new front-end interface. (convert_for_assignment): Likewise. (convert_for_condition): Likewise. (d_array_convert): Likewise. * d-diagnostic.cc (error): Remove. (errorSupplemental): Remove. (warning): Remove. (warningSupplemental): Remove. (deprecation): Remove. (deprecationSupplemental): Remove. (message): Remove. (vtip): New. * d-frontend.cc (global): Remove. (Global::_init): Remove. (Global::startGagging): Remove. (Global::endGagging): Remove. (Global::increaseErrorCount): Remove. (Loc::Loc): Remove. (Loc::toChars): Remove. (Loc::equals): Remove. (isBuiltin): Update for new front-end interface. (eval_builtin): Likewise. (getTypeInfoType): Likewise. (inlineCopy): Remove. * d-incpath.cc: Include d-frontend.h. (add_globalpaths): Call d_gc_malloc to allocate Strings. (add_filepaths): Likewise. * d-lang.cc: Include dmd/id.h, dmd/root/file.h, d-frontend.h. Remove include of dmd/mars.h, id.h. (entrypoint_module): Remove. (entrypoint_root_module): Remove. (deps_write_string): Update for new front-end interface. (deps_write): Likewise. (d_init_options): Call rt_init. Remove setting global params that are default initialized by the front-end. (d_handle_option): Handle OPT_fcheckaction_, OPT_fdump_c___spec_, OPT_fdump_c___spec_verbose, OPT_fextern_std_, OPT_fpreview, OPT_revert, OPT_fsave_mixins_, and OPT_ftransition. (d_post_options): Propagate dip1021 and dip1000 preview flags to dip25, and flag_diagnostics_show_caret to printErrorContext. (d_add_entrypoint_module): Remove. (d_parse_file): Update for new front-end interface. (d_type_promotes_to): Likewise. (d_types_compatible_p): Likewise. * d-longdouble.cc (CTFloat::zero): Remove. (CTFloat::one): Remove. (CTFloat::minusone): Remove. (CTFloat::half): Remove. * d-system.h (POSIX): Remove. (realpath): Remove. (isalpha): Remove. (isalnum): Remove. (isdigit): Remove. (islower): Remove. (isprint): Remove. (isspace): Remove. (isupper): Remove. (isxdigit): Remove. (tolower): Remove. (_mkdir): Remove. (INT32_MAX): Remove. (INT32_MIN): Remove. (INT64_MIN): Remove. (UINT32_MAX): Remove. (UINT64_MAX): Remove. * d-target.cc: Include calls.h. (target): Remove. (define_float_constants): Remove initialization of snan. (Target::_init): Update for new front-end interface. (Target::isVectorTypeSupported): Likewise. (Target::isVectorOpSupported): Remove cases for unordered operators. (TargetCPP::typeMangle): Update for new front-end interface. (TargetCPP::parameterType): Likewise. (Target::systemLinkage): Likewise. (Target::isReturnOnStack): Likewise. (Target::isCalleeDestroyingArgs): Define. (Target::preferPassByRef): Define. * d-tree.h (d_add_entrypoint_module): Remove. * decl.cc (gcc_attribute_p): Update for new front-end interface. (apply_pragma_crt): Define. (DeclVisitor::visit(PragmaDeclaration *)): Handle pragmas crt_constructor and crt_destructor. (DeclVisitor::visit(TemplateDeclaration *)): Update for new front-end interface. (DeclVisitor::visit): Likewise. (DeclVisitor::finish_vtable): Likewise. (get_symbol_decl): Error if template has more than one nesting context. Update for new front-end interface. (make_thunk): Update for new front-end interface. (get_vtable_decl): Likewise. * expr.cc (ExprVisitor::visit): Likewise. (build_return_dtor): Likewise. * imports.cc (ImportVisitor::visit): Likewise. * intrinsics.cc: Include dmd/expression.h. Remove include of dmd/mangle.h. (maybe_set_intrinsic): Update for new front-end interface. * intrinsics.def (INTRINSIC_ROL): Update intrinsic signature. (INTRINSIC_ROR): Likewise. (INTRINSIC_ROR_TIARG): Likewise. (INTRINSIC_TOPREC): Likewise. (INTRINSIC_TOPRECL): Likewise. (INTRINSIC_TAN): Update intrinsic module and signature. (INTRINSIC_ISNAN): Likewise. (INTRINSIC_ISFINITE): Likewise. (INTRINSIC_COPYSIGN): Define intrinsic. (INTRINSIC_COPYSIGNI): Define intrinsic. (INTRINSIC_EXP): Update intrinsic module. (INTRINSIC_EXPM1): Likewise. (INTRINSIC_EXP2): Likewise. (INTRINSIC_LOG): Likewise. (INTRINSIC_LOG2): Likewise. (INTRINSIC_LOG10): Likewise. (INTRINSIC_POW): Likewise. (INTRINSIC_ROUND): Likewise. (INTRINSIC_FLOORF): Likewise. (INTRINSIC_FLOOR): Likewise. (INTRINSIC_FLOORL): Likewise. (INTRINSIC_CEILF): Likewise. (INTRINSIC_CEIL): Likewise. (INTRINSIC_CEILL): Likewise. (INTRINSIC_TRUNC): Likewise. (INTRINSIC_FMIN): Likewise. (INTRINSIC_FMAX): Likewise. (INTRINSIC_FMA): Likewise. (INTRINSIC_VA_ARG): Update intrinsic signature. (INTRINSIC_VASTART): Likewise. * lang.opt (fcheck=): Add alternate aliases for contract switches. (fcheckaction=): New option. (check_action): New Enum and EnumValue entries. (fdump-c++-spec-verbose): New option. (fdump-c++-spec=): New option. (fextern-std=): New option. (extern_stdcpp): New Enum and EnumValue entries (fpreview=): New options. (frevert=): New options. (fsave-mixins): New option. (ftransition=): Update options. * modules.cc (get_internal_fn): Replace Prot with Visibility. (build_internal_fn): Likewise. (build_dso_cdtor_fn): Likewise. (build_module_tree): Remove check for __entrypoint module. * runtime.def (P5): Define. (ARRAYBOUNDS_SLICEP): Define. (ARRAYBOUNDS_INDEXP): Define. (NEWTHROW): Define. (ADCMP2): Remove. (ARRAYCAST): Remove. (SWITCH_STRING): Remove. (SWITCH_USTRING): Remove. (SWITCH_DSTRING): Remove. (SWITCH_ERROR): Remove. * toir.cc (IRVisitor::visit): Update for new front-end interface. (IRVisitor::check_previous_goto): Remove checks for case and default statements. (IRVisitor::visit(SwitchStatement *)): Remove handling of string switch conditions. * typeinfo.cc: Include d-frontend.h. (get_typeinfo_kind): Update for new front-end interface. (make_frontend_typeinfo): Likewise. (TypeInfoVisitor::visit): Likewise. (builtin_typeinfo_p): Likewise. (get_typeinfo_decl): Likewise. (build_typeinfo): Likewise. * types.cc (valist_array_p): Likewise. (make_array_type): Likewise. (merge_aggregate_types): Likewise. (TypeVisitor::visit(TypeBasic *)): Likewise. (TypeVisitor::visit(TypeFunction *)): Likewise. (TypeVisitor::visit(TypeStruct *)): Update comment. * verstr.h: Removed. * d-frontend.h: New file. gcc/po/ChangeLog: * EXCLUDES: Remove d/dmd sources from list. gcc/testsuite/ChangeLog: * gdc.dg/Wcastresult2.d: Update test. * gdc.dg/asm1.d: Likewise. * gdc.dg/asm2.d: Likewise. * gdc.dg/asm3.d: Likewise. * gdc.dg/gdc282.d: Likewise. * gdc.dg/imports/gdc170.d: Likewise. * gdc.dg/intrinsics.d: Likewise. * gdc.dg/pr101672.d: Likewise. * gdc.dg/pr90650a.d: Likewise. * gdc.dg/pr90650b.d: Likewise. * gdc.dg/pr94777a.d: Likewise. * gdc.dg/pr95250.d: Likewise. * gdc.dg/pr96869.d: Likewise. * gdc.dg/pr98277.d: Likewise. * gdc.dg/pr98457.d: Likewise. * gdc.dg/simd1.d: Likewise. * gdc.dg/simd2a.d: Likewise. * gdc.dg/simd2b.d: Likewise. * gdc.dg/simd2c.d: Likewise. * gdc.dg/simd2d.d: Likewise. * gdc.dg/simd2e.d: Likewise. * gdc.dg/simd2f.d: Likewise. * gdc.dg/simd2g.d: Likewise. * gdc.dg/simd2h.d: Likewise. * gdc.dg/simd2i.d: Likewise. * gdc.dg/simd2j.d: Likewise. * gdc.dg/simd7951.d: Likewise. * gdc.dg/torture/gdc309.d: Likewise. * gdc.dg/torture/pr94424.d: Likewise. * gdc.dg/torture/pr94777b.d: Likewise. * lib/gdc-utils.exp (gdc-convert-args): Handle new compiler options. (gdc-convert-test): Handle CXXFLAGS, EXTRA_OBJC_SOURCES, and ARG_SETS test directives. (gdc-do-test): Only import modules in the test run directory. * gdc.dg/pr94777c.d: New test. * gdc.dg/pr96156b.d: New test. * gdc.dg/pr96157c.d: New test. * gdc.dg/simd_ctfe.d: New test. * gdc.dg/torture/simd17344.d: New test. * gdc.dg/torture/simd20052.d: New test. * gdc.dg/torture/simd6.d: New test. * gdc.dg/torture/simd7.d: New test. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime e6caaab9. * libdruntime/Makefile.am (D_EXTRA_FLAGS): Build libdruntime with -fpreview=dip1000, -fpreview=fieldwise, and -fpreview=dtorfields. (ALL_DRUNTIME_SOURCES): Add DRUNTIME_DSOURCES_STDCXX. (DRUNTIME_DSOURCES): Update list of C binding modules. (DRUNTIME_DSOURCES_STDCXX): Likewise. (DRUNTIME_DSOURCES_LINUX): Likewise. (DRUNTIME_DSOURCES_OPENBSD): Likewise. (DRUNTIME_DISOURCES): Remove __entrypoint.di. * libdruntime/Makefile.in: Regenerated. * libdruntime/__entrypoint.di: Removed. * libdruntime/gcc/deh.d (_d_isbaseof): Update signature. (_d_createTrace): Likewise. (__gdc_begin_catch): Remove reference to the exception. (_d_throw): Increment reference count of thrown object before unwind. (__gdc_personality): Chain exceptions with Throwable.chainTogether. * libdruntime/gcc/emutls.d: Update imports. * libdruntime/gcc/sections/elf.d: Update imports. (DSO.moduleGroup): Update signature. * libdruntime/gcc/sections/macho.d: Update imports. (DSO.moduleGroup): Update signature. * libdruntime/gcc/sections/pecoff.d: Update imports. (DSO.moduleGroup): Update signature. * src/MERGE: Merge upstream phobos 5ab9ad256. * src/Makefile.am (D_EXTRA_DFLAGS): Add -fpreview=dip1000 and -fpreview=dtorfields flags. (PHOBOS_DSOURCES): Update list of std modules. * src/Makefile.in: Regenerate. * testsuite/lib/libphobos.exp (libphobos-dg-test): Handle assembly compile types. (dg-test): Override. (additional_prunes): Define. (libphobos-dg-prune): Filter any additional_prunes set by tests. * testsuite/libphobos.aa/test_aa.d: Update test. * testsuite/libphobos.druntime/druntime.exp (version_flags): Add -fversion=CoreUnittest. * testsuite/libphobos.druntime_shared/druntime_shared.exp (version_flags): Add -fversion=CoreUnittest -fversion=Shared. * testsuite/libphobos.exceptions/unknown_gc.d: Update test. * testsuite/libphobos.hash/test_hash.d: Update test. * testsuite/libphobos.phobos/phobos.exp (version_flags): Add -fversion=StdUnittest * testsuite/libphobos.phobos_shared/phobos_shared.exp (version_flags): Likewise. * testsuite/libphobos.shared/host.c: Update test. * testsuite/libphobos.shared/load.d: Update test. * testsuite/libphobos.shared/load_13414.d: Update test. * testsuite/libphobos.thread/fiber_guard_page.d: Update test. * testsuite/libphobos.thread/tlsgc_sections.d: Update test. * testsuite/testsuite_flags.in: Add -fpreview=dip1000 to --gdcflags. * testsuite/libphobos.shared/link_mod_collision.d: Removed. * testsuite/libphobos.shared/load_mod_collision.d: Removed. * testsuite/libphobos.betterc/betterc.exp: New test. * testsuite/libphobos.config/config.exp: New test. * testsuite/libphobos.gc/gc.exp: New test. * testsuite/libphobos.imports/imports.exp: New test. * testsuite/libphobos.lifetime/lifetime.exp: New test. * testsuite/libphobos.unittest/unittest.exp: New test.
Diffstat (limited to 'libphobos/src/std/array.d')
-rw-r--r--libphobos/src/std/array.d2036
1 files changed, 1502 insertions, 534 deletions
diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d
index 179fa66..ded1196 100644
--- a/libphobos/src/std/array.d
+++ b/libphobos/src/std/array.d
@@ -5,50 +5,51 @@ Functions and types that manipulate built-in arrays and associative arrays.
This module provides all kinds of functions to create, manipulate or convert arrays:
$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
$(BOOKTABLE ,
$(TR $(TH Function Name) $(TH Description)
)
- $(TR $(TD $(LREF _array))
- $(TD Returns a copy of the input in a newly allocated dynamic _array.
+ $(TR $(TD $(LREF array))
+ $(TD Returns a copy of the input in a newly allocated dynamic array.
))
$(TR $(TD $(LREF appender))
- $(TD Returns a new $(LREF Appender) or $(LREF RefAppender) initialized with a given _array.
+ $(TD Returns a new $(LREF Appender) or $(LREF RefAppender) initialized with a given array.
))
$(TR $(TD $(LREF assocArray))
- $(TD Returns a newly allocated associative _array from a range of key/value tuples.
+ $(TD Returns a newly allocated associative array from a range of key/value tuples.
))
$(TR $(TD $(LREF byPair))
- $(TD Construct a range iterating over an associative _array by key/value tuples.
+ $(TD Construct a range iterating over an associative array by key/value tuples.
))
$(TR $(TD $(LREF insertInPlace))
- $(TD Inserts into an existing _array at a given position.
+ $(TD Inserts into an existing array at a given position.
))
$(TR $(TD $(LREF join))
- $(TD Concatenates a range of ranges into one _array.
+ $(TD Concatenates a range of ranges into one array.
))
$(TR $(TD $(LREF minimallyInitializedArray))
- $(TD Returns a new _array of type $(D T).
+ $(TD Returns a new array of type `T`.
))
$(TR $(TD $(LREF replace))
- $(TD Returns a new _array with all occurrences of a certain subrange replaced.
+ $(TD Returns a new array with all occurrences of a certain subrange replaced.
))
$(TR $(TD $(LREF replaceFirst))
- $(TD Returns a new _array with the first occurrence of a certain subrange replaced.
+ $(TD Returns a new array with the first occurrence of a certain subrange replaced.
))
$(TR $(TD $(LREF replaceInPlace))
- $(TD Replaces all occurrences of a certain subrange and puts the result into a given _array.
+ $(TD Replaces all occurrences of a certain subrange and puts the result into a given array.
))
$(TR $(TD $(LREF replaceInto))
$(TD Replaces all occurrences of a certain subrange and puts the result into an output range.
))
$(TR $(TD $(LREF replaceLast))
- $(TD Returns a new _array with the last occurrence of a certain subrange replaced.
+ $(TD Returns a new array with the last occurrence of a certain subrange replaced.
))
$(TR $(TD $(LREF replaceSlice))
- $(TD Returns a new _array with a given slice replaced.
+ $(TD Returns a new array with a given slice replaced.
))
$(TR $(TD $(LREF replicate))
- $(TD Creates a new _array out of several copies of an input _array or range.
+ $(TD Creates a new array out of several copies of an input array or range.
))
$(TR $(TD $(LREF sameHead))
$(TD Checks if the initial segments of two arrays refer to the same
@@ -59,20 +60,24 @@ $(TR $(TH Function Name) $(TH Description)
in memory.
))
$(TR $(TD $(LREF split))
- $(TD Eagerly split a range or string into an _array.
+ $(TD Eagerly split a range or string into an array.
+ ))
+ $(TR $(TD $(LREF staticArray))
+ $(TD Creates a new static array from given data.
))
$(TR $(TD $(LREF uninitializedArray))
- $(TD Returns a new _array of type $(D T) without initializing its elements.
+ $(TD Returns a new array of type `T` without initializing its elements.
))
-)
+))
Copyright: Copyright Andrei Alexandrescu 2008- and Jonathan M Davis 2011-.
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Authors: $(HTTP erdani.org, Andrei Alexandrescu) and Jonathan M Davis
+Authors: $(HTTP erdani.org, Andrei Alexandrescu) and
+ $(HTTP jmdavisprog.com, Jonathan M Davis)
-Source: $(PHOBOSSRC std/_array.d)
+Source: $(PHOBOSSRC std/array.d)
*/
module std.array;
@@ -85,17 +90,19 @@ public import std.range.primitives : save, empty, popFront, popBack, front, back
/**
* Allocates an array and initializes it with copies of the elements
- * of range $(D r).
+ * of range `r`.
*
- * Narrow strings are handled as a special case in an overload.
+ * Narrow strings are handled as follows:
+ * - If autodecoding is turned on (default), then they are handled as a separate overload.
+ * - If autodecoding is turned off, then this is equivalent to duplicating the array.
*
* Params:
- * r = range (or aggregate with $(D opApply) function) whose elements are copied into the allocated array
+ * r = range (or aggregate with `opApply` function) whose elements are copied into the allocated array
* Returns:
* allocated and initialized array
*/
ForeachType!Range[] array(Range)(Range r)
-if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range)
+if (isIterable!Range && !isAutodecodableString!Range && !isInfinite!Range)
{
if (__ctfe)
{
@@ -114,7 +121,7 @@ if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range)
if (length == 0)
return null;
- import std.conv : emplaceRef;
+ import core.internal.lifetime : emplaceRef;
auto result = (() @trusted => uninitializedArray!(Unqual!E[])(length))();
@@ -138,6 +145,13 @@ if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range)
}
}
+/// ditto
+ForeachType!(PointerTarget!Range)[] array(Range)(Range r)
+if (isPointer!Range && isIterable!(PointerTarget!Range) && !isAutodecodableString!Range && !isInfinite!Range)
+{
+ return array(*r);
+}
+
///
@safe pure nothrow unittest
{
@@ -156,13 +170,33 @@ if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range)
assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
}
-@system unittest
+@safe pure nothrow unittest
+{
+ struct MyRange
+ {
+ enum front = 123;
+ enum empty = true;
+ void popFront() {}
+ }
+
+ auto arr = (new MyRange).array;
+ assert(arr.empty);
+}
+
+@safe pure nothrow unittest
+{
+ immutable int[] a = [1, 2, 3, 4];
+ auto b = (&a).array;
+ assert(b == a);
+}
+
+@safe pure nothrow unittest
{
import std.algorithm.comparison : equal;
struct Foo
{
int a;
- void opAssign(Foo foo)
+ noreturn opAssign(Foo)
{
assert(0);
}
@@ -175,57 +209,102 @@ if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range)
assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
}
-@safe unittest
+// https://issues.dlang.org/show_bug.cgi?id=12315
+@safe pure nothrow unittest
{
- // Issue 12315
static struct Bug12315 { immutable int i; }
enum bug12315 = [Bug12315(123456789)].array();
static assert(bug12315[0].i == 123456789);
}
-@safe unittest
+@safe pure nothrow unittest
{
import std.range;
static struct S{int* p;}
auto a = array(immutable(S).init.repeat(5));
+ assert(a.length == 5);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18995
+@system unittest
+{
+ import core.memory : __delete;
+ int nAlive = 0;
+ struct S
+ {
+ bool alive;
+ this(int) { alive = true; ++nAlive; }
+ this(this) { nAlive += alive; }
+ ~this() { nAlive -= alive; alive = false; }
+ }
+
+ import std.algorithm.iteration : map;
+ import std.range : iota;
+
+ auto arr = iota(3).map!(a => S(a)).array;
+ assert(nAlive == 3);
+
+ // No good way to ensure the GC frees this, just call the lifetime function
+ // directly.
+ __delete(arr);
+
+ assert(nAlive == 0);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ //Turn down infinity:
+ static assert(!is(typeof(
+ repeat(1).array()
+ )));
}
/**
-Convert a narrow string to an array type that fully supports random access.
-This is handled as a special case and always returns an array of `dchar`
+Convert a narrow autodecoding string to an array type that fully supports
+random access. This is handled as a special case and always returns an array
+of `dchar`
+
+NOTE: This function is never used when autodecoding is turned off.
Params:
str = `isNarrowString` to be converted to an array of `dchar`
Returns:
- a $(D dchar[]), $(D const(dchar)[]), or $(D immutable(dchar)[]) depending on the constness of
+ a `dchar[]`, `const(dchar)[]`, or `immutable(dchar)[]` depending on the constness of
the input.
*/
-@trusted ElementType!String[] array(String)(scope String str)
-if (isNarrowString!String)
+CopyTypeQualifiers!(ElementType!String,dchar)[] array(String)(scope String str)
+if (isAutodecodableString!String)
{
import std.utf : toUTF32;
- /* This function is @trusted because the following cast
- * is unsafe - converting a dstring[] to a dchar[], for example.
- * Our special knowledge of toUTF32 is needed to know the cast is safe.
+ auto temp = str.toUTF32;
+ /* Unsafe cast. Allowed because toUTF32 makes a new array
+ and copies all the elements.
*/
- return cast(typeof(return)) str.toUTF32;
+ return () @trusted { return cast(CopyTypeQualifiers!(ElementType!String, dchar)[]) temp; } ();
}
///
-@safe unittest
+@safe pure nothrow unittest
{
import std.range.primitives : isRandomAccessRange;
+ import std.traits : isAutodecodableString;
- assert("Hello D".array == "Hello D"d);
- static assert(isRandomAccessRange!string == false);
+ // note that if autodecoding is turned off, `array` will not transcode these.
+ static if (isAutodecodableString!string)
+ assert("Hello D".array == "Hello D"d);
+ else
+ assert("Hello D".array == "Hello D");
+
+ static if (isAutodecodableString!wstring)
+ assert("Hello D"w.array == "Hello D"d);
+ else
+ assert("Hello D"w.array == "Hello D"w);
- assert("Hello D"w.array == "Hello D"d);
static assert(isRandomAccessRange!dstring == true);
}
-@system unittest
+@safe unittest
{
- // @system due to array!string
import std.conv : to;
static struct TestArray { int x; string toString() @safe { return to!string(x); } }
@@ -242,7 +321,7 @@ if (isNarrowString!String)
static struct OpApply
{
- int opApply(scope int delegate(ref int) dg)
+ int opApply(scope int delegate(ref int) @safe dg)
{
int res;
foreach (i; 0 .. 10)
@@ -256,11 +335,10 @@ if (isNarrowString!String)
}
auto a = array([1, 2, 3, 4, 5][]);
- //writeln(a);
assert(a == [ 1, 2, 3, 4, 5 ]);
auto b = array([TestArray(1), TestArray(2)][]);
- //writeln(b);
+ assert(b == [TestArray(1), TestArray(2)]);
class C
{
@@ -269,23 +347,27 @@ if (isNarrowString!String)
override string toString() const @safe { return to!string(x); }
}
auto c = array([new C(1), new C(2)][]);
- //writeln(c);
+ assert(c[0].x == 1);
+ assert(c[1].x == 2);
auto d = array([1.0, 2.2, 3][]);
assert(is(typeof(d) == double[]));
- //writeln(d);
+ assert(d == [1.0, 2.2, 3]);
auto e = [OpAssign(1), OpAssign(2)];
auto f = array(e);
assert(e == f);
assert(array(OpApply.init) == [0,1,2,3,4,5,6,7,8,9]);
- assert(array("ABC") == "ABC"d);
- assert(array("ABC".dup) == "ABC"d.dup);
+ static if (isAutodecodableString!string)
+ {
+ assert(array("ABC") == "ABC"d);
+ assert(array("ABC".dup) == "ABC"d.dup);
+ }
}
-//Bug# 8233
-@safe unittest
+// https://issues.dlang.org/show_bug.cgi?id=8233
+@safe pure nothrow unittest
{
assert(array("hello world"d) == "hello world"d);
immutable a = [1, 2, 3, 4, 5];
@@ -305,16 +387,16 @@ if (isNarrowString!String)
int i;
}
- foreach (T; AliasSeq!(S, const S, immutable S))
- {
+ static foreach (T; AliasSeq!(S, const S, immutable S))
+ {{
auto arr = [T(1), T(2), T(3), T(4)];
assert(array(arr) == arr);
- }
+ }}
}
-@safe unittest
+// https://issues.dlang.org/show_bug.cgi?id=9824
+@safe pure nothrow unittest
{
- //9824
static struct S
{
@disable void opAssign(S);
@@ -324,8 +406,8 @@ if (isNarrowString!String)
arr.array();
}
-// Bugzilla 10220
-@safe unittest
+// https://issues.dlang.org/show_bug.cgi?id=10220
+@safe pure nothrow unittest
{
import std.algorithm.comparison : equal;
import std.exception;
@@ -345,25 +427,26 @@ if (isNarrowString!String)
});
}
-@safe unittest
-{
- //Turn down infinity:
- static assert(!is(typeof(
- repeat(1).array()
- )));
-}
-
/**
-Returns a newly allocated associative _array from a range of key/value tuples.
+Returns a newly allocated associative array from a range of key/value tuples
+or from a range of keys and a range of values.
Params:
r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
of tuples of keys and values.
-Returns: A newly allocated associative array out of elements of the input
-range, which must be a range of tuples (Key, Value). Returns a null associative
-array reference when given an empty range.
-Duplicates: Associative arrays have unique keys. If r contains duplicate keys,
-then the result will contain the value of the last pair for that key in r.
+ keys = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of keys
+ values = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of values
+
+Returns:
+
+ A newly allocated associative array out of elements of the input
+ range, which must be a range of tuples (Key, Value) or
+ a range of keys and a range of values. If given two ranges of unequal
+ lengths after the elements of the shorter are exhausted the remaining
+ elements of the longer will not be considered.
+ Returns a null associative array reference when given an empty range.
+ Duplicates: Associative arrays have unique keys. If r contains duplicate keys,
+ then the result will contain the value of the last pair for that key in r.
See_Also: $(REF Tuple, std,typecons), $(REF zip, std,range)
*/
@@ -374,7 +457,8 @@ if (isInputRange!Range)
import std.typecons : isTuple;
alias E = ElementType!Range;
- static assert(isTuple!E, "assocArray: argument must be a range of tuples");
+ static assert(isTuple!E, "assocArray: argument must be a range of tuples,"
+ ~" but was a range of "~E.stringof);
static assert(E.length == 2, "assocArray: tuple dimension must be 2");
alias KeyType = E.Types[0];
alias ValueType = E.Types[1];
@@ -386,57 +470,228 @@ if (isInputRange!Range)
return aa;
}
+/// ditto
+auto assocArray(Keys, Values)(Keys keys, Values values)
+if (isInputRange!Values && isInputRange!Keys)
+{
+ static if (isDynamicArray!Keys && isDynamicArray!Values
+ && !isNarrowString!Keys && !isNarrowString!Values)
+ {
+ void* aa;
+ {
+ // aaLiteral is nothrow when the destructors don't throw
+ static if (is(typeof(() nothrow
+ {
+ import std.range : ElementType;
+ import std.traits : hasElaborateDestructor;
+ alias KeyElement = ElementType!Keys;
+ static if (hasElaborateDestructor!KeyElement)
+ KeyElement.init.__xdtor();
+
+ alias ValueElement = ElementType!Values;
+ static if (hasElaborateDestructor!ValueElement)
+ ValueElement.init.__xdtor();
+ })))
+ {
+ scope(failure) assert(false, "aaLiteral must not throw");
+ }
+ if (values.length > keys.length)
+ values = values[0 .. keys.length];
+ else if (keys.length > values.length)
+ keys = keys[0 .. values.length];
+ aa = aaLiteral(keys, values);
+ }
+ alias Key = typeof(keys[0]);
+ alias Value = typeof(values[0]);
+ return (() @trusted => cast(Value[Key]) aa)();
+ }
+ else
+ {
+ // zip is not always able to infer nothrow
+ alias Key = ElementType!Keys;
+ alias Value = ElementType!Values;
+ static assert(isMutable!Value, "assocArray: value type must be mutable");
+ Value[Key] aa;
+ foreach (key; keys)
+ {
+ if (values.empty) break;
+
+ // aa[key] is incorrectly not @safe if the destructor throws
+ // https://issues.dlang.org/show_bug.cgi?id=18592
+ static if (is(typeof(() @safe
+ {
+ import std.range : ElementType;
+ import std.traits : hasElaborateDestructor;
+ alias KeyElement = ElementType!Keys;
+ static if (hasElaborateDestructor!KeyElement)
+ KeyElement.init.__xdtor();
+
+ alias ValueElement = ElementType!Values;
+ static if (hasElaborateDestructor!ValueElement)
+ ValueElement.init.__xdtor();
+ })))
+ {
+ () @trusted {
+ aa[key] = values.front;
+ }();
+ }
+ else
+ {
+ aa[key] = values.front;
+ }
+ values.popFront();
+ }
+ return aa;
+ }
+}
+
///
@safe pure /*nothrow*/ unittest
{
- import std.range;
- import std.typecons;
+ import std.range : repeat, zip;
+ import std.typecons : tuple;
+ import std.range.primitives : autodecodeStrings;
auto a = assocArray(zip([0, 1, 2], ["a", "b", "c"])); // aka zipMap
- assert(is(typeof(a) == string[int]));
+ static assert(is(typeof(a) == string[int]));
assert(a == [0:"a", 1:"b", 2:"c"]);
auto b = assocArray([ tuple("foo", "bar"), tuple("baz", "quux") ]);
- assert(is(typeof(b) == string[string]));
+ static assert(is(typeof(b) == string[string]));
assert(b == ["foo":"bar", "baz":"quux"]);
+
+ static if (autodecodeStrings)
+ alias achar = dchar;
+ else
+ alias achar = immutable(char);
+ auto c = assocArray("ABCD", true.repeat);
+ static assert(is(typeof(c) == bool[achar]));
+ bool[achar] expected = ['D':true, 'A':true, 'B':true, 'C':true];
+ assert(c == expected);
}
-// @@@11053@@@ - Cannot be version (unittest) - recursive instantiation error
-@safe unittest
+// Cannot be version (StdUnittest) - recursive instantiation error
+// https://issues.dlang.org/show_bug.cgi?id=11053
+@safe pure nothrow unittest
{
import std.typecons;
+ static assert(!__traits(compiles, [ 1, 2, 3 ].assocArray()));
static assert(!__traits(compiles, [ tuple("foo", "bar", "baz") ].assocArray()));
static assert(!__traits(compiles, [ tuple("foo") ].assocArray()));
assert([ tuple("foo", "bar") ].assocArray() == ["foo": "bar"]);
}
-// Issue 13909
-@safe unittest
+// https://issues.dlang.org/show_bug.cgi?id=13909
+@safe pure nothrow unittest
{
import std.typecons;
auto a = [tuple!(const string, string)("foo", "bar")];
auto b = [tuple!(string, const string)("foo", "bar")];
+ assert(a == b);
assert(assocArray(a) == [cast(const(string)) "foo": "bar"]);
static assert(!__traits(compiles, assocArray(b)));
}
+// https://issues.dlang.org/show_bug.cgi?id=5502
+@safe pure nothrow unittest
+{
+ auto a = assocArray([0, 1, 2], ["a", "b", "c"]);
+ static assert(is(typeof(a) == string[int]));
+ assert(a == [0:"a", 1:"b", 2:"c"]);
+
+ auto b = assocArray([0, 1, 2], [3L, 4, 5]);
+ static assert(is(typeof(b) == long[int]));
+ assert(b == [0: 3L, 1: 4, 2: 5]);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=5502
+@safe pure unittest
+{
+ import std.algorithm.iteration : filter, map;
+ import std.range : enumerate;
+ import std.range.primitives : autodecodeStrings;
+
+ auto r = "abcde".enumerate.filter!(a => a.index == 2);
+ auto a = assocArray(r.map!(a => a.value), r.map!(a => a.index));
+ static if (autodecodeStrings)
+ alias achar = dchar;
+ else
+ alias achar = immutable(char);
+ static assert(is(typeof(a) == size_t[achar]));
+ assert(a == [achar('c'): size_t(2)]);
+}
+
+@safe nothrow pure unittest
+{
+ import std.range : iota;
+ auto b = assocArray(3.iota, 3.iota(6));
+ static assert(is(typeof(b) == int[int]));
+ assert(b == [0: 3, 1: 4, 2: 5]);
+
+ b = assocArray([0, 1, 2], [3, 4, 5]);
+ assert(b == [0: 3, 1: 4, 2: 5]);
+}
+
+@safe unittest
+{
+ struct ThrowingElement
+ {
+ int i;
+ static bool b;
+ ~this(){
+ if (b)
+ throw new Exception("");
+ }
+ }
+ static assert(!__traits(compiles, () nothrow { assocArray([ThrowingElement()], [0]);}));
+ assert(assocArray([ThrowingElement()], [0]) == [ThrowingElement(): 0]);
+
+ static assert(!__traits(compiles, () nothrow { assocArray([0], [ThrowingElement()]);}));
+ assert(assocArray([0], [ThrowingElement()]) == [0: ThrowingElement()]);
+
+ import std.range : iota;
+ static assert(!__traits(compiles, () nothrow { assocArray(1.iota, [ThrowingElement()]);}));
+ assert(assocArray(1.iota, [ThrowingElement()]) == [0: ThrowingElement()]);
+}
+
+@system unittest
+{
+ import std.range : iota;
+ struct UnsafeElement
+ {
+ int i;
+ static bool b;
+ ~this(){
+ int[] arr;
+ void* p = arr.ptr + 1; // unsafe
+ }
+ }
+ static assert(!__traits(compiles, () @safe { assocArray(1.iota, [UnsafeElement()]);}));
+ assert(assocArray(1.iota, [UnsafeElement()]) == [0: UnsafeElement()]);
+}
+
/**
Construct a range iterating over an associative array by key/value tuples.
-Params: aa = The associative array to iterate over.
+Params:
+ aa = The associative array to iterate over.
-Returns: A $(REF_ALTTEXT forward range, isForwardRange, std,_range,primitives)
-of Tuple's of key and value pairs from the given associative array.
+Returns: A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+of Tuple's of key and value pairs from the given associative array. The members
+of each pair can be accessed by name (`.key` and `.value`). or by integer
+index (0 and 1 respectively).
*/
-auto byPair(AA : Value[Key], Value, Key)(AA aa)
+auto byPair(AA)(AA aa)
+if (isAssociativeArray!AA)
{
import std.algorithm.iteration : map;
import std.typecons : tuple;
- return aa.byKeyValue.map!(pair => tuple(pair.key, pair.value));
+ return aa.byKeyValue
+ .map!(pair => tuple!("key", "value")(pair.key, pair.value));
}
///
-@system unittest
+@safe pure nothrow unittest
{
import std.algorithm.sorting : sort;
import std.typecons : tuple, Tuple;
@@ -447,27 +702,33 @@ auto byPair(AA : Value[Key], Value, Key)(AA aa)
// Iteration over key/value pairs.
foreach (pair; aa.byPair)
{
- pairs ~= pair;
+ if (pair.key == "b")
+ pairs ~= tuple("B", pair.value);
+ else
+ pairs ~= pair;
}
// Iteration order is implementation-dependent, so we should sort it to get
// a fixed order.
- sort(pairs);
+ pairs.sort();
assert(pairs == [
+ tuple("B", 2),
tuple("a", 1),
- tuple("b", 2),
tuple("c", 3)
]);
}
-@system unittest
+@safe pure nothrow unittest
{
import std.typecons : tuple, Tuple;
+ import std.meta : AliasSeq;
auto aa = ["a":2];
auto pairs = aa.byPair();
- static assert(is(typeof(pairs.front) == Tuple!(string,int)));
+ alias PT = typeof(pairs.front);
+ static assert(is(PT : Tuple!(string,int)));
+ static assert(PT.fieldNames == AliasSeq!("key", "value"));
static assert(isForwardRange!(typeof(pairs)));
assert(!pairs.empty);
@@ -481,8 +742,8 @@ auto byPair(AA : Value[Key], Value, Key)(AA aa)
assert(savedPairs.front == tuple("a", 2));
}
-// Issue 17711
-@system unittest
+// https://issues.dlang.org/show_bug.cgi?id=17711
+@safe pure nothrow unittest
{
const(int[string]) aa = [ "abc": 123 ];
@@ -513,7 +774,8 @@ private template blockAttribute(T)
enum blockAttribute = GC.BlkAttr.NO_SCAN;
}
}
-version (unittest)
+
+@safe unittest
{
import core.memory : UGC = GC;
static assert(!(blockAttribute!void & UGC.BlkAttr.NO_SCAN));
@@ -532,22 +794,28 @@ private template nDimensions(T)
}
}
-version (unittest)
+@safe unittest
{
static assert(nDimensions!(uint[]) == 1);
static assert(nDimensions!(float[][]) == 2);
}
/++
-Returns a new array of type $(D T) allocated on the garbage collected heap
-without initializing its elements. This can be a useful optimization if every
-element will be immediately initialized. $(D T) may be a multidimensional
-array. In this case sizes may be specified for any number of dimensions from 0
-to the number in $(D T).
+Returns a new array of type `T` allocated on the garbage collected heap
+without initializing its elements. This can be a useful optimization if every
+element will be immediately initialized. `T` may be a multidimensional
+array. In this case sizes may be specified for any number of dimensions from 0
+to the number in `T`.
+
+uninitializedArray is `nothrow` and weakly `pure`.
-uninitializedArray is nothrow and weakly pure.
+uninitializedArray is `@system` if the uninitialized element type has pointers.
-uninitializedArray is @system if the uninitialized element type has pointers.
+Params:
+ T = The type of the resulting array elements
+ sizes = The length dimension(s) of the resulting array
+Returns:
+ An array of `T` with `I.length` dimensions.
+/
auto uninitializedArray(T, I...)(I sizes) nothrow @system
if (isDynamicArray!T && allSatisfy!(isIntegral, I) && hasIndirections!(ElementEncodingType!T))
@@ -596,13 +864,19 @@ if (isDynamicArray!T && allSatisfy!(isIntegral, I) && !hasIndirections!(ElementE
}
/++
-Returns a new array of type $(D T) allocated on the garbage collected heap.
+Returns a new array of type `T` allocated on the garbage collected heap.
Partial initialization is done for types with indirections, for preservation
of memory safety. Note that elements will only be initialized to 0, but not
-necessarily the element type's $(D .init).
+necessarily the element type's `.init`.
+
+minimallyInitializedArray is `nothrow` and weakly `pure`.
-minimallyInitializedArray is nothrow and weakly pure.
+Params:
+ T = The type of the array elements
+ sizes = The length dimension(s) of the resulting array
+Returns:
+ An array of `T` with `I.length` dimensions.
+/
auto minimallyInitializedArray(T, I...)(I sizes) nothrow @trusted
if (isDynamicArray!T && allSatisfy!(isIntegral, I))
@@ -627,8 +901,12 @@ if (isDynamicArray!T && allSatisfy!(isIntegral, I))
auto arr = minimallyInitializedArray!(int[])(42);
assert(arr.length == 42);
- // Elements aren't necessarily initialized to 0
- assert(!arr.equal(0.repeat(42)));
+
+ // Elements aren't necessarily initialized to 0, so don't do this:
+ // assert(arr.equal(0.repeat(42)));
+ // If that is needed, initialize the array normally instead:
+ auto arr2 = new int[42];
+ assert(arr2.equal(0.repeat(42)));
}
@safe pure nothrow unittest
@@ -645,6 +923,9 @@ if (isDynamicArray!T && allSatisfy!(isIntegral, I))
}
}
+// from rt/lifetime.d
+private extern(C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow;
+
private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow
{
static assert(I.length <= nDimensions!T,
@@ -656,7 +937,8 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow
static if (I.length != 0)
{
- static assert(is(I[0] == size_t));
+ static assert(is(I[0] == size_t), "I[0] must be of type size_t not "
+ ~ I[0].stringof);
alias size = sizes[0];
}
@@ -676,7 +958,7 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow
ret ~= E.init;
}
catch (Exception e)
- throw new Error(e.msg);
+ assert(0, e.msg);
}
else
assert(0, "No postblit nor default init on " ~ E.stringof ~
@@ -684,18 +966,24 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow
}
else
{
- import core.memory : GC;
import core.stdc.string : memset;
- import core.checkedint : mulu;
- bool overflow;
- const nbytes = mulu(size, E.sizeof, overflow);
- if (overflow) assert(0);
-
- auto ptr = cast(E*) GC.malloc(nbytes, blockAttribute!E);
+ /+
+ NOTES:
+ _d_newarrayU is part of druntime, and creates an uninitialized
+ block, just like GC.malloc. However, it also sets the appropriate
+ bits, and sets up the block as an appendable array of type E[],
+ which will inform the GC how to destroy the items in the block
+ when it gets collected.
+
+ _d_newarrayU returns a void[], but with the length set according
+ to E.sizeof.
+ +/
+ *(cast(void[]*)&ret) = _d_newarrayU(typeid(E[]), size);
static if (minimallyInitialized && hasIndirections!E)
- memset(ptr, 0, nbytes);
- ret = ptr[0 .. size];
+ // _d_newarrayU would have asserted if the multiplication below
+ // had overflowed, so we don't have to check it again.
+ memset(ret.ptr, 0, E.sizeof * ret.length);
}
}
else static if (I.length > 1)
@@ -716,7 +1004,8 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow
assert(s2.length == 0);
}
-@safe nothrow pure unittest //@@@9803@@@
+// https://issues.dlang.org/show_bug.cgi?id=9803
+@safe nothrow pure unittest
{
auto a = minimallyInitializedArray!(int*[])(1);
assert(a[0] == null);
@@ -726,7 +1015,8 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow
assert(c[0][0] == null);
}
-@safe unittest //@@@10637@@@
+// https://issues.dlang.org/show_bug.cgi?id=10637
+@safe pure nothrow unittest
{
static struct S
{
@@ -743,15 +1033,24 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow
}
~this()
{
- assert(p != null);
+ // note, this assert is invalid -- a struct should always be able
+ // to run its dtor on the .init value, I'm leaving it here
+ // commented out because the original test case had it. I'm not
+ // sure what it's trying to prove.
+ //
+ // What happens now that minimallyInitializedArray adds the
+ // destructor run to the GC, is that this assert would fire in the
+ // GC, which triggers an invalid memory operation.
+ //assert(p != null);
}
}
auto a = minimallyInitializedArray!(S[])(1);
assert(a[0].p == null);
enum b = minimallyInitializedArray!(S[])(1);
+ assert(b[0].p == null);
}
-@safe nothrow unittest
+@safe pure nothrow unittest
{
static struct S1
{
@@ -759,42 +1058,63 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow
this(this) @disable;
}
auto a1 = minimallyInitializedArray!(S1[][])(2, 2);
- //enum b1 = minimallyInitializedArray!(S1[][])(2, 2);
+ assert(a1);
static struct S2
{
this() @disable;
//this(this) @disable;
}
auto a2 = minimallyInitializedArray!(S2[][])(2, 2);
+ assert(a2);
enum b2 = minimallyInitializedArray!(S2[][])(2, 2);
+ assert(b2);
static struct S3
{
//this() @disable;
this(this) @disable;
}
auto a3 = minimallyInitializedArray!(S3[][])(2, 2);
+ assert(a3);
enum b3 = minimallyInitializedArray!(S3[][])(2, 2);
+ assert(b3);
}
-// overlap
-/*
-NOTE: Undocumented for now, overlap does not yet work with ctfe.
-Returns the overlapping portion, if any, of two arrays. Unlike $(D
-equal), $(D overlap) only compares the pointers in the ranges, not the
-values referred by them. If $(D r1) and $(D r2) have an overlapping
-slice, returns that slice. Otherwise, returns the null slice.
-*/
-auto overlap(T, U)(T[] r1, U[] r2) @trusted pure nothrow
-if (is(typeof(r1.ptr < r2.ptr) == bool))
+/++
+Returns the overlapping portion, if any, of two arrays. Unlike `equal`,
+`overlap` only compares the pointers and lengths in the
+ranges, not the values referred by them. If `r1` and `r2` have an
+overlapping slice, returns that slice. Otherwise, returns the null
+slice.
+
+Params:
+ a = The first array to compare
+ b = The second array to compare
+Returns:
+ The overlapping portion of the two arrays.
++/
+CommonType!(T[], U[]) overlap(T, U)(T[] a, U[] b) @trusted
+if (is(typeof(a.ptr < b.ptr) == bool))
{
- import std.algorithm.comparison : min, max;
- auto b = max(r1.ptr, r2.ptr);
- auto e = min(r1.ptr + r1.length, r2.ptr + r2.length);
- return b < e ? b[0 .. e - b] : null;
+ import std.algorithm.comparison : min;
+
+ auto end = min(a.ptr + a.length, b.ptr + b.length);
+ // CTFE requires pairing pointer comparisons, which forces a
+ // slightly inefficient implementation.
+ if (a.ptr <= b.ptr && b.ptr < a.ptr + a.length)
+ {
+ return b.ptr[0 .. end - b.ptr];
+ }
+
+ if (b.ptr <= a.ptr && a.ptr < b.ptr + b.length)
+ {
+ return a.ptr[0 .. end - a.ptr];
+ }
+
+ return null;
}
///
-@safe pure /*nothrow*/ unittest
+@safe pure nothrow unittest
{
int[] a = [ 10, 11, 12, 13, 14 ];
int[] b = a[1 .. 3];
@@ -802,15 +1122,22 @@ if (is(typeof(r1.ptr < r2.ptr) == bool))
b = b.dup;
// overlap disappears even though the content is the same
assert(overlap(a, b).empty);
+
+ static test()() @nogc
+ {
+ auto a = "It's three o'clock"d;
+ auto b = a[5 .. 10];
+ return b.overlap(a);
+ }
+
+ //works at compile-time
+ static assert(test == "three"d);
}
-@safe /*nothrow*/ unittest
+@safe pure nothrow unittest
{
static void test(L, R)(L l, R r)
{
- import std.stdio;
- scope(failure) writeln("Types: L %s R %s", L.stringof, R.stringof);
-
assert(overlap(l, r) == [ 100, 12 ]);
assert(overlap(l, l[0 .. 2]) is l[0 .. 2]);
@@ -829,10 +1156,11 @@ if (is(typeof(r1.ptr < r2.ptr) == bool))
test(a, b);
assert(overlap(a, b.dup).empty);
test(c, d);
- assert(overlap(c, d.idup).empty);
+ assert(overlap(c, d.dup.idup).empty);
}
-@safe pure nothrow unittest // bugzilla 9836
+ // https://issues.dlang.org/show_bug.cgi?id=9836
+@safe pure nothrow unittest
{
// range primitives for array should work with alias this types
struct Wrapper
@@ -854,8 +1182,10 @@ if (is(typeof(r1.ptr < r2.ptr) == bool))
private void copyBackwards(T)(T[] src, T[] dest)
{
import core.stdc.string : memmove;
+ import std.format : format;
- assert(src.length == dest.length);
+ assert(src.length == dest.length, format!
+ "src.length %s must equal dest.length %s"(src.length, dest.length));
if (!__ctfe || hasElaborateCopyConstructor!T)
{
@@ -876,14 +1206,14 @@ private void copyBackwards(T)(T[] src, T[] dest)
}
/++
- Inserts $(D stuff) (which must be an input range or any number of
- implicitly convertible items) in $(D array) at position $(D pos).
+ Inserts `stuff` (which must be an input range or any number of
+ implicitly convertible items) in `array` at position `pos`.
Params:
- array = The array that $(D stuff) will be inserted into.
- pos = The position in $(D array) to insert the $(D stuff).
+ array = The array that `stuff` will be inserted into.
+ pos = The position in `array` to insert the `stuff`.
stuff = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives),
- or any number of implicitly convertible items to insert into $(D array).
+ or any number of implicitly convertible items to insert into `array`.
+/
void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff)
if (!isSomeString!(T[])
@@ -891,7 +1221,7 @@ if (!isSomeString!(T[])
{
static if (allSatisfy!(isInputRangeWithLengthOrConvertible!T, U))
{
- import std.conv : emplaceRef;
+ import core.internal.lifetime : emplaceRef;
immutable oldLen = array.length;
@@ -950,7 +1280,7 @@ if (isSomeString!(T[]) && allSatisfy!(isCharOrStringOrDcharRange, U))
static if (is(Unqual!T == T)
&& allSatisfy!(isInputRangeWithLengthOrConvertible!dchar, U))
{
- import std.utf : codeLength;
+ import std.utf : codeLength, byDchar;
// mutable, can do in place
//helper function: re-encode dchar to Ts and store at *ptr
static T* putDChar(T* ptr, dchar ch)
@@ -994,7 +1324,8 @@ if (isSomeString!(T[]) && allSatisfy!(isCharOrStringOrDcharRange, U))
@trusted static void moveToRight(T[] arr, size_t gap)
{
- static assert(!hasElaborateCopyConstructor!T);
+ static assert(!hasElaborateCopyConstructor!T,
+ "T must not have an elaborate copy constructor");
import core.stdc.string : memmove;
if (__ctfe)
{
@@ -1014,7 +1345,7 @@ if (isSomeString!(T[]) && allSatisfy!(isCharOrStringOrDcharRange, U))
}
else
{
- foreach (dchar ch; stuff[i])
+ foreach (ch; stuff[i].byDchar)
ptr = putDChar(ptr, ch);
}
}
@@ -1040,6 +1371,41 @@ if (isSomeString!(T[]) && allSatisfy!(isCharOrStringOrDcharRange, U))
assert(a == [ 1, 2, 1, 2, 3, 4 ]);
a.insertInPlace(3, 10u, 11);
assert(a == [ 1, 2, 1, 10, 11, 2, 3, 4]);
+
+ union U
+ {
+ float a = 3.0;
+ int b;
+ }
+
+ U u1 = { b : 3 };
+ U u2 = { b : 4 };
+ U u3 = { b : 5 };
+ U[] unionArr = [u2, u3];
+ unionArr.insertInPlace(2, [u1]);
+ assert(unionArr == [u2, u3, u1]);
+ unionArr.insertInPlace(0, [u3, u2]);
+ assert(unionArr == [u3, u2, u2, u3, u1]);
+
+ static class C
+ {
+ int a;
+ float b;
+
+ this(int a, float b) { this.a = a; this.b = b; }
+ }
+
+ C c1 = new C(42, 1.0);
+ C c2 = new C(0, 0.0);
+ C c3 = new C(int.max, float.init);
+
+ C[] classArr = [c1, c2, c3];
+ insertInPlace(classArr, 3, [c2, c3]);
+ C[5] classArr1 = classArr;
+ assert(classArr1 == [c1, c2, c3, c2, c3]);
+ insertInPlace(classArr, 0, c3, c1);
+ C[7] classArr2 = classArr;
+ assert(classArr2 == [c3, c1, c1, c2, c3, c2, c3]);
}
//constraint helpers
@@ -1081,8 +1447,7 @@ private template isInputRangeOrConvertible(E)
import std.exception;
- bool test(T, U, V)(T orig, size_t pos, U toInsert, V result,
- string file = __FILE__, size_t line = __LINE__)
+ bool test(T, U, V)(T orig, size_t pos, U toInsert, V result)
{
{
static if (is(T == typeof(T.init.dup)))
@@ -1127,10 +1492,10 @@ private template isInputRangeOrConvertible(E)
new AssertError("testStr failure 3", file, line));
}
- foreach (T; AliasSeq!(char, wchar, dchar,
+ static foreach (T; AliasSeq!(char, wchar, dchar,
immutable(char), immutable(wchar), immutable(dchar)))
{
- foreach (U; AliasSeq!(char, wchar, dchar,
+ static foreach (U; AliasSeq!(char, wchar, dchar,
immutable(char), immutable(wchar), immutable(dchar)))
{
testStr!(T[], U[])();
@@ -1198,18 +1563,6 @@ private template isInputRangeOrConvertible(E)
assert(arr[0] == 1);
insertInPlace(arr, 1, Int(2), Int(3));
assert(equal(arr, [1, 2, 3, 4, 5])); //check it works with postblit
-
- version (none) // illustrates that insertInPlace() will not work with CTFE and postblit
- {
- static bool testctfe()
- {
- Int[] arr = [Int(1), Int(4), Int(5)];
- assert(arr[0] == 1);
- insertInPlace(arr, 1, Int(2), Int(3));
- return equal(arr, [1, 2, 3, 4, 5]); //check it works with postblit
- }
- enum E = testctfe();
- }
}
@safe unittest
@@ -1224,7 +1577,8 @@ private template isInputRangeOrConvertible(E)
});
}
-@system unittest // bugzilla 6874
+// https://issues.dlang.org/show_bug.cgi?id=6874
+@system unittest
{
import core.memory;
// allocate some space
@@ -1244,9 +1598,15 @@ private template isInputRangeOrConvertible(E)
/++
- Returns whether the $(D front)s of $(D lhs) and $(D rhs) both refer to the
+ Returns whether the `front`s of `lhs` and `rhs` both refer to the
same place in memory, making one of the arrays a slice of the other which
- starts at index $(D 0).
+ starts at index `0`.
+
+ Params:
+ lhs = the first array to compare
+ rhs = the second array to compare
+ Returns:
+ `true` if $(D lhs.ptr == rhs.ptr), `false` otherwise.
+/
@safe
pure nothrow bool sameHead(T)(in T[] lhs, in T[] rhs)
@@ -1265,9 +1625,16 @@ pure nothrow bool sameHead(T)(in T[] lhs, in T[] rhs)
/++
- Returns whether the $(D back)s of $(D lhs) and $(D rhs) both refer to the
+ Returns whether the `back`s of `lhs` and `rhs` both refer to the
same place in memory, making one of the arrays a slice of the other which
- end at index $(D $).
+ end at index `$`.
+
+ Params:
+ lhs = the first array to compare
+ rhs = the second array to compare
+ Returns:
+ `true` if both arrays are the same length and $(D lhs.ptr == rhs.ptr),
+ `false` otherwise.
+/
@trusted
pure nothrow bool sameTail(T)(in T[] lhs, in T[] rhs)
@@ -1286,8 +1653,8 @@ pure nothrow bool sameTail(T)(in T[] lhs, in T[] rhs)
@safe pure nothrow unittest
{
- foreach (T; AliasSeq!(int[], const(int)[], immutable(int)[], const int[], immutable int[]))
- {
+ static foreach (T; AliasSeq!(int[], const(int)[], immutable(int)[], const int[], immutable int[]))
+ {{
T a = [1, 2, 3, 4, 5];
T b = a;
T c = a[1 .. $];
@@ -1309,7 +1676,7 @@ pure nothrow bool sameTail(T)(in T[] lhs, in T[] rhs)
//verifies R-value compatibilty
assert(a.sameHead(a[0 .. 0]));
assert(a.sameTail(a[$ .. $]));
- }
+ }}
}
/**
@@ -1380,9 +1747,8 @@ if (isInputRange!S && !isDynamicArray!S)
{
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
- {
- S s;
+ static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
+ {{
immutable S t = "abc";
assert(replicate(to!S("1234"), 0) is null);
@@ -1392,24 +1758,31 @@ if (isInputRange!S && !isDynamicArray!S)
assert(replicate(to!S("1"), 4) == "1111");
assert(replicate(t, 3) == "abcabcabc");
assert(replicate(cast(S) null, 4) is null);
- }
+ }}
}
/++
-Eagerly split the string $(D s) into an array of words, using whitespace as
-delimiter. Runs of whitespace are merged together (no empty words are produced).
+Eagerly splits `range` into an array, using `sep` as the delimiter.
-$(D @safe), $(D pure) and $(D CTFE)-able.
+When no delimiter is provided, strings are split into an array of words,
+using whitespace as delimiter.
+Runs of whitespace are merged together (no empty words are produced).
+
+The `range` must be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives).
+The separator can be a value of the same type as the elements in `range`
+or it can be another forward `range`.
Params:
- s = the string to split
+ s = the string to split by word if no separator is given
+ range = the range to split
+ sep = a value of the same type as the elements of `range` or another
+ isTerminator = a predicate that splits the range when it returns `true`.
Returns:
- An array of each word in `s`
+ An array containing the divided parts of `range` (or the words of `s`).
See_Also:
-$(REF splitter, std,algorithm,iteration) for a version that splits using any
-separator.
+$(REF splitter, std,algorithm,iteration) for a lazy version without allocating memory.
$(REF splitter, std,regex) for a version that splits using a regular
expression defined separator.
@@ -1419,7 +1792,7 @@ if (isSomeString!S)
{
size_t istart;
bool inword = false;
- S[] result;
+ auto result = appender!(S[]);
foreach (i, dchar c ; s)
{
@@ -1428,7 +1801,7 @@ if (isSomeString!S)
{
if (inword)
{
- result ~= s[istart .. i];
+ put(result, s[istart .. i]);
inword = false;
}
}
@@ -1442,45 +1815,40 @@ if (isSomeString!S)
}
}
if (inword)
- result ~= s[istart .. $];
- return result;
+ put(result, s[istart .. $]);
+ return result.data;
}
///
@safe unittest
{
- string str = "Hello World!";
- assert(str.split == ["Hello", "World!"]);
-
- string str2 = "Hello\t\tWorld\t!";
- assert(str2.split == ["Hello", "World", "!"]);
+ import std.uni : isWhite;
+ assert("Learning,D,is,fun".split(",") == ["Learning", "D", "is", "fun"]);
+ assert("Learning D is fun".split!isWhite == ["Learning", "D", "is", "fun"]);
+ assert("Learning D is fun".split(" D ") == ["Learning", "is fun"]);
}
-/**
- * `split` allocates memory, so the same effect can be achieved lazily
- * using $(REF splitter, std,algorithm,iteration).
- */
+///
@safe unittest
{
- import std.ascii : isWhite;
- import std.algorithm.comparison : equal;
- import std.algorithm.iteration : splitter;
-
string str = "Hello World!";
- assert(str.splitter!(isWhite).equal(["Hello", "World!"]));
+ assert(str.split == ["Hello", "World!"]);
+
+ string str2 = "Hello\t\tWorld\t!";
+ assert(str2.split == ["Hello", "World", "!"]);
}
@safe unittest
{
import std.conv : to;
- import std.format;
+ import std.format : format;
import std.typecons;
static auto makeEntry(S)(string l, string[] r)
{return tuple(l.to!S(), r.to!(S[])());}
- foreach (S; AliasSeq!(string, wstring, dstring,))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring,))
+ {{
auto entries =
[
makeEntry!S("", []),
@@ -1495,7 +1863,7 @@ if (isSomeString!S)
];
foreach (entry; entries)
assert(entry[0].split() == entry[1], format("got: %s, expected: %s.", entry[0].split(), entry[1]));
- }
+ }}
//Just to test that an immutable is split-able
immutable string s = " \t\npeter paul\tjerry \n";
@@ -1524,43 +1892,12 @@ if (isSomeString!S)
assert(a == [[1], [4, 5, 1], [4, 5]]);
}
-/++
- Eagerly splits $(D range) into an array, using $(D sep) as the delimiter.
-
- The _range must be a
- $(REF_ALTTEXT forward _range, isForwardRange, std,_range,primitives).
- The separator can be a value of the same type as the elements in $(D range)
- or it can be another forward _range.
-
- Example:
- If $(D range) is a $(D string), $(D sep) can be a $(D char) or another
- $(D string). The return type will be an array of strings. If $(D range) is
- an $(D int) array, $(D sep) can be an $(D int) or another $(D int) array.
- The return type will be an array of $(D int) arrays.
-
- Params:
- range = a forward _range.
- sep = a value of the same type as the elements of $(D range) or another
- forward range.
-
- Returns:
- An array containing the divided parts of $(D range).
-
- See_Also:
- $(REF splitter, std,algorithm,iteration) for the lazy version of this
- function.
- +/
-auto split(Range, Separator)(Range range, Separator sep)
-if (isForwardRange!Range && is(typeof(ElementType!Range.init == Separator.init)))
-{
- import std.algorithm.iteration : splitter;
- return range.splitter(sep).array;
-}
///ditto
auto split(Range, Separator)(Range range, Separator sep)
-if (
- isForwardRange!Range && isForwardRange!Separator
- && is(typeof(ElementType!Range.init == ElementType!Separator.init)))
+if (isForwardRange!Range && (
+ is(typeof(ElementType!Range.init == Separator.init)) ||
+ is(typeof(ElementType!Range.init == ElementType!Separator.init)) && isForwardRange!Separator
+ ))
{
import std.algorithm.iteration : splitter;
return range.splitter(sep).array;
@@ -1573,26 +1910,17 @@ if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(range.front))))
return range.splitter!isTerminator.array;
}
-///
-@safe unittest
-{
- import std.uni : isWhite;
- assert("Learning,D,is,fun".split(",") == ["Learning", "D", "is", "fun"]);
- assert("Learning D is fun".split!isWhite == ["Learning", "D", "is", "fun"]);
- assert("Learning D is fun".split(" D ") == ["Learning", "is fun"]);
-}
-
@safe unittest
{
import std.algorithm.comparison : cmp;
import std.conv;
- foreach (S; AliasSeq!(string, wstring, dstring,
+ static foreach (S; AliasSeq!(string, wstring, dstring,
immutable(string), immutable(wstring), immutable(dstring),
char[], wchar[], dchar[],
const(char)[], const(wchar)[], const(dchar)[],
const(char[]), immutable(char[])))
- {
+ {{
S s = to!S(",peter,paul,jerry,");
auto words = split(s, ",");
@@ -1632,14 +1960,14 @@ if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(range.front))))
words = split(s5, ",,");
assert(words.length == 3);
assert(cmp(words[0], "peter") == 0);
- }
+ }}
}
-/++
+/+
Conservative heuristic to determine if a range can be iterated cheaply.
- Used by $(D join) in decision to do an extra iteration of the range to
+ Used by `join` in decision to do an extra iteration of the range to
compute the resultant length. If iteration is not cheap then precomputing
- length could be more expensive than using $(D Appender).
+ length could be more expensive than using `Appender`.
For now, we only assume arrays are cheap to iterate.
+/
@@ -1660,11 +1988,13 @@ private enum bool hasCheapIteration(R) = isArray!R;
See_Also:
For a lazy version, see $(REF joiner, std,algorithm,iteration)
+/
-ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, scope R sep)
+ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, R sep)
if (isInputRange!RoR &&
isInputRange!(Unqual!(ElementType!RoR)) &&
isInputRange!R &&
- is(Unqual!(ElementType!(ElementType!RoR)) == Unqual!(ElementType!R)))
+ (is(immutable ElementType!(ElementType!RoR) == immutable ElementType!R) ||
+ (isSomeChar!(ElementType!(ElementType!RoR)) && isSomeChar!(ElementType!R))
+ ))
{
alias RetType = typeof(return);
alias RetTypeElement = Unqual!(ElementEncodingType!RetType);
@@ -1677,7 +2007,7 @@ if (isInputRange!RoR &&
// This converts sep to an array (forward range) if it isn't one,
// and makes sure it has the same string encoding for string types.
static if (isSomeString!RetType &&
- !is(RetTypeElement == Unqual!(ElementEncodingType!R)))
+ !is(immutable ElementEncodingType!RetType == immutable ElementEncodingType!R))
{
import std.conv : to;
auto sepArr = to!RetType(sep);
@@ -1689,7 +2019,7 @@ if (isInputRange!RoR &&
static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem))
{
- import std.conv : emplaceRef;
+ import core.internal.lifetime : emplaceRef;
size_t length; // length of result array
size_t rorLength; // length of range ror
foreach (r; ror.save)
@@ -1723,24 +2053,50 @@ if (isInputRange!RoR &&
ror.popFront();
for (; !ror.empty; ror.popFront())
{
- put(result, sep);
+ put(result, sepArr);
put(result, ror.front);
}
return result.data;
}
}
-@safe unittest // Issue 14230
+// https://issues.dlang.org/show_bug.cgi?id=14230
+@safe unittest
{
string[] ary = ["","aa","bb","cc"]; // leaded by _empty_ element
assert(ary.join(" @") == " @aa @bb @cc"); // OK in 2.067b1 and olders
}
+// https://issues.dlang.org/show_bug.cgi?id=21337
+@system unittest
+{
+ import std.algorithm.iteration : map;
+
+ static class Once
+ {
+ bool empty;
+
+ void popFront()
+ {
+ empty = true;
+ }
+
+ int front()
+ {
+ return 0;
+ }
+ }
+
+ assert([1, 2].map!"[a]".join(new Once) == [1, 0, 2]);
+}
+
/// Ditto
ElementEncodingType!(ElementType!RoR)[] join(RoR, E)(RoR ror, scope E sep)
if (isInputRange!RoR &&
isInputRange!(Unqual!(ElementType!RoR)) &&
- is(E : ElementType!(ElementType!RoR)))
+ ((is(E : ElementType!(ElementType!RoR))) ||
+ (!autodecodeStrings && isSomeChar!(ElementType!(ElementType!RoR)) &&
+ isSomeChar!E)))
{
alias RetType = typeof(return);
alias RetTypeElement = Unqual!(ElementEncodingType!RetType);
@@ -1760,7 +2116,8 @@ if (isInputRange!RoR &&
}
else
{
- import std.conv : emplaceRef;
+ import core.internal.lifetime : emplaceRef;
+ import std.format : format;
size_t length;
size_t rorLength;
foreach (r; ror.save)
@@ -1784,7 +2141,8 @@ if (isInputRange!RoR &&
foreach (e; r)
emplaceRef(result[len++], e);
}
- assert(len == result.length);
+ assert(len == result.length, format!
+ "len %s must equal result.lenght %s"(len, result.length));
return (() @trusted => cast(RetType) result)();
}
}
@@ -1802,7 +2160,8 @@ if (isInputRange!RoR &&
}
}
-@safe unittest // Issue 10895
+// https://issues.dlang.org/show_bug.cgi?id=10895
+@safe unittest
{
class A
{
@@ -1814,9 +2173,11 @@ if (isInputRange!RoR &&
assert(a[0].length == 3);
auto temp = join(a, " ");
assert(a[0].length == 3);
+ assert(temp.length == 3);
}
-@safe unittest // Issue 14230
+// https://issues.dlang.org/show_bug.cgi?id=14230
+@safe unittest
{
string[] ary = ["","aa","bb","cc"];
assert(ary.join('@') == "@aa@bb@cc");
@@ -1828,7 +2189,15 @@ if (isInputRange!RoR &&
isInputRange!(Unqual!(ElementType!RoR)))
{
alias RetType = typeof(return);
- alias RetTypeElement = Unqual!(ElementEncodingType!RetType);
+ alias ConstRetTypeElement = ElementEncodingType!RetType;
+ static if (isAssignable!(Unqual!ConstRetTypeElement, ConstRetTypeElement))
+ {
+ alias RetTypeElement = Unqual!ConstRetTypeElement;
+ }
+ else
+ {
+ alias RetTypeElement = ConstRetTypeElement;
+ }
alias RoRElem = ElementType!RoR;
if (ror.empty)
@@ -1836,7 +2205,7 @@ if (isInputRange!RoR &&
static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem))
{
- import std.conv : emplaceRef;
+ import core.internal.lifetime : emplaceRef;
size_t length;
foreach (r; ror.save)
length += r.length;
@@ -1845,8 +2214,9 @@ if (isInputRange!RoR &&
size_t len;
foreach (r; ror)
foreach (e; r)
- emplaceRef(result[len++], e);
- assert(len == result.length);
+ emplaceRef!RetTypeElement(result[len++], e);
+ assert(len == result.length,
+ "emplaced an unexpected number of elements");
return (() @trusted => cast(RetType) result)();
}
else
@@ -1875,50 +2245,61 @@ if (isInputRange!RoR &&
@safe pure unittest
{
import std.conv : to;
+ import std.range.primitives : autodecodeStrings;
- foreach (T; AliasSeq!(string,wstring,dstring))
- {
+ static foreach (T; AliasSeq!(string,wstring,dstring))
+ {{
auto arr2 = "Здравствуй Мир Unicode".to!(T);
auto arr = ["Здравствуй", "Мир", "Unicode"].to!(T[]);
assert(join(arr) == "ЗдравствуйМирUnicode");
- foreach (S; AliasSeq!(char,wchar,dchar))
- {
+ static foreach (S; AliasSeq!(char,wchar,dchar))
+ {{
auto jarr = arr.join(to!S(' '));
static assert(is(typeof(jarr) == T));
assert(jarr == arr2);
- }
- foreach (S; AliasSeq!(string,wstring,dstring))
- {
+ }}
+ static foreach (S; AliasSeq!(string,wstring,dstring))
+ {{
auto jarr = arr.join(to!S(" "));
static assert(is(typeof(jarr) == T));
assert(jarr == arr2);
- }
- }
+ }}
+ }}
- foreach (T; AliasSeq!(string,wstring,dstring))
- {
+ static foreach (T; AliasSeq!(string,wstring,dstring))
+ {{
auto arr2 = "Здравствуй\u047CМир\u047CUnicode".to!(T);
auto arr = ["Здравствуй", "Мир", "Unicode"].to!(T[]);
- foreach (S; AliasSeq!(wchar,dchar))
- {
+ static foreach (S; AliasSeq!(wchar,dchar))
+ {{
auto jarr = arr.join(to!S('\u047C'));
static assert(is(typeof(jarr) == T));
assert(jarr == arr2);
- }
- }
+ }}
+ }}
const string[] arr = ["apple", "banana"];
assert(arr.join(',') == "apple,banana");
}
-@system unittest
+@safe unittest
+{
+ class A { }
+
+ const A[][] array;
+ auto result = array.join; // can't remove constness, so don't try
+
+ static assert(is(typeof(result) == const(A)[]));
+}
+
+@safe unittest
{
import std.algorithm;
import std.conv : to;
import std.range;
- foreach (R; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (R; AliasSeq!(string, wstring, dstring))
+ {{
R word1 = "日本語";
R word2 = "paul";
R word3 = "jerry";
@@ -1934,8 +2315,8 @@ if (isInputRange!RoR &&
auto filteredLenWordsArr = [filteredLenWord1, filteredLenWord2, filteredLenWord3];
auto filteredWords = filter!"true"(filteredWordsArr);
- foreach (S; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
assert(join(filteredWords, to!S(", ")) == "日本語, paul, jerry");
assert(join(filteredWords, to!(ElementType!S)(',')) == "日本語,paul,jerry");
assert(join(filteredWordsArr, to!(ElementType!(S))(',')) == "日本語,paul,jerry");
@@ -1969,7 +2350,7 @@ if (isInputRange!RoR &&
assert(join(filteredLenWordsArr, filterComma) == "日本語, paul, jerry");
assert(join(filter!"true"(words), filterComma) == "日本語, paul, jerry");
assert(join(words, filterComma) == "日本語, paul, jerry");
- }();
+ }}
assert(join(filteredWords) == "日本語pauljerry");
assert(join(filteredWordsArr) == "日本語pauljerry");
@@ -1995,7 +2376,7 @@ if (isInputRange!RoR &&
assert(join(filter!"true"(cast(R[])[])).empty);
assert(join(cast(R[])[]).empty);
- }
+ }}
assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]);
assert(join([[1, 2], [41, 42]], cast(int[])[]) == [1, 2, 41, 42]);
@@ -2016,7 +2397,7 @@ if (isInputRange!RoR &&
assert(join(f([f([1, 2]), f([41, 42])]), f([5, 6])) == [1, 2, 5, 6, 41, 42]);
}
-// Issue 10683
+// https://issues.dlang.org/show_bug.cgi?id=10683
@safe unittest
{
import std.range : join;
@@ -2025,7 +2406,7 @@ if (isInputRange!RoR &&
assert([[tuple("x")]].join == [tuple("x")]);
}
-// Issue 13877
+// https://issues.dlang.org/show_bug.cgi?id=13877
@safe unittest
{
// Test that the range is iterated only once.
@@ -2048,40 +2429,49 @@ if (isInputRange!RoR &&
/++
- Replace occurrences of `from` with `to` in `subject` in a new
- array. If `sink` is defined, then output the new array into
- `sink`.
+ Replace occurrences of `from` with `to` in `subject` in a new array.
Params:
- sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)
subject = the array to scan
from = the item to replace
to = the item to replace all instances of `from` with
Returns:
- If `sink` isn't defined, a new array without changing the
- contents of `subject`, or the original array if no match
- is found.
+ A new array without changing the contents of `subject`, or the original
+ array if no match is found.
See_Also:
- $(REF map, std,algorithm,iteration) which can act as a lazy replace
+ $(REF substitute, std,algorithm,iteration) for a lazy replace.
+/
E[] replace(E, R1, R2)(E[] subject, R1 from, R2 to)
-if (isDynamicArray!(E[]) && isForwardRange!R1 && isForwardRange!R2
- && (hasLength!R2 || isSomeString!R2))
+if ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
+ is(Unqual!E : Unqual!R1))
{
import std.algorithm.searching : find;
+ import std.range : dropOne;
- if (from.empty) return subject;
+ static if (isInputRange!R1)
+ {
+ if (from.empty) return subject;
+ alias rSave = a => a.save;
+ }
+ else
+ {
+ alias rSave = a => a;
+ }
- auto balance = find(subject, from.save);
+ auto balance = find(subject, rSave(from));
if (balance.empty)
return subject;
auto app = appender!(E[])();
app.put(subject[0 .. subject.length - balance.length]);
- app.put(to.save);
- replaceInto(app, balance[from.length .. $], from, to);
+ app.put(rSave(to));
+ // replacing an element in an array is different to a range replacement
+ static if (is(Unqual!E : Unqual!R1))
+ replaceInto(app, balance.dropOne, from, to);
+ else
+ replaceInto(app, balance[from.length .. $], from, to);
return app.data;
}
@@ -2093,30 +2483,98 @@ if (isDynamicArray!(E[]) && isForwardRange!R1 && isForwardRange!R2
assert("Hello Wörld".replace("l", "h") == "Hehho Wörhd");
}
-/// ditto
+@safe unittest
+{
+ assert([1, 2, 3, 4, 2].replace([2], [5]) == [1, 5, 3, 4, 5]);
+ assert([3, 3, 3].replace([3], [0]) == [0, 0, 0]);
+ assert([3, 3, 4, 3].replace([3, 3], [1, 1, 1]) == [1, 1, 1, 4, 3]);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18215
+@safe unittest
+{
+ auto arr = ["aaa.dd", "b"];
+ arr = arr.replace("aaa.dd", ".");
+ assert(arr == [".", "b"]);
+
+ arr = ["_", "_", "aaa.dd", "b", "c", "aaa.dd", "e"];
+ arr = arr.replace("aaa.dd", ".");
+ assert(arr == ["_", "_", ".", "b", "c", ".", "e"]);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18215
+@safe unittest
+{
+ assert([[0], [1, 2], [0], [3]].replace([0], [4]) == [[4], [1, 2], [4], [3]]);
+ assert([[0], [1, 2], [0], [3], [1, 2]]
+ .replace([1, 2], [0]) == [[0], [0], [0], [3], [0]]);
+ assert([[0], [1, 2], [0], [3], [1, 2], [0], [1, 2]]
+ .replace([[0], [1, 2]], [[4]]) == [[4], [0], [3], [1, 2], [4]]);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=10930
+@safe unittest
+{
+ assert([0, 1, 2].replace(1, 4) == [0, 4, 2]);
+ assert("äbö".replace('ä', 'a') == "abö");
+}
+
+// empty array
+@safe unittest
+{
+ int[] arr;
+ assert(replace(arr, 1, 2) == []);
+}
+
+/++
+ Replace occurrences of `from` with `to` in `subject` and output the result into
+ `sink`.
+
+ Params:
+ sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)
+ subject = the array to scan
+ from = the item to replace
+ to = the item to replace all instances of `from` with
+
+ See_Also:
+ $(REF substitute, std,algorithm,iteration) for a lazy replace.
+ +/
void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to)
-if (isOutputRange!(Sink, E) && isDynamicArray!(E[])
- && isForwardRange!R1 && isForwardRange!R2
- && (hasLength!R2 || isSomeString!R2))
+if (isOutputRange!(Sink, E) &&
+ ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
+ is(Unqual!E : Unqual!R1)))
{
import std.algorithm.searching : find;
+ import std.range : dropOne;
- if (from.empty)
+ static if (isInputRange!R1)
+ {
+ if (from.empty)
+ {
+ sink.put(subject);
+ return;
+ }
+ alias rSave = a => a.save;
+ }
+ else
{
- sink.put(subject);
- return;
+ alias rSave = a => a;
}
for (;;)
{
- auto balance = find(subject, from.save);
+ auto balance = find(subject, rSave(from));
if (balance.empty)
{
sink.put(subject);
break;
}
sink.put(subject[0 .. subject.length - balance.length]);
- sink.put(to.save);
- subject = balance[from.length .. $];
+ sink.put(rSave(to));
+ // replacing an element in an array is different to a range replacement
+ static if (is(Unqual!E : Unqual!R1))
+ subject = balance.dropOne;
+ else
+ subject = balance[from.length .. $];
}
}
@@ -2133,15 +2591,24 @@ if (isOutputRange!(Sink, E) && isDynamicArray!(E[])
assert(sink.data == [1, 4, 6, 4, 5]);
}
+// empty array
+@safe unittest
+{
+ auto sink = appender!(int[])();
+ int[] arr;
+ replaceInto(sink, arr, 1, 2);
+ assert(sink.data == []);
+}
+
@safe unittest
{
import std.algorithm.comparison : cmp;
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
+ static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
{
- foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
+ {{
auto s = to!S("This is a foo foo list");
auto from = to!T("foo");
auto into = to!S("silly");
@@ -2157,7 +2624,7 @@ if (isOutputRange!(Sink, E) && isDynamicArray!(E[])
assert(i == 0);
assert(replace(r, to!S("won't find this"), to!S("whatever")) is r);
- }();
+ }}
}
immutable s = "This is a foo foo list";
@@ -2175,15 +2642,27 @@ if (isOutputRange!(Sink, E) && isDynamicArray!(E[])
this(C[] arr){ desired = arr; }
void put(C[] part){ assert(skipOver(desired, part)); }
}
- foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
+ {{
alias Char = ElementEncodingType!S;
S s = to!S("yet another dummy text, yet another ...");
S from = to!S("yet another");
S into = to!S("some");
replaceInto(CheckOutput!(Char)(to!S("some dummy text, some ..."))
, s, from, into);
- }
+ }}
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=10930
+@safe unittest
+{
+ auto sink = appender!(int[])();
+ replaceInto(sink, [0, 1, 2], 1, 5);
+ assert(sink.data == [0, 5, 2]);
+
+ auto sink2 = appender!(dchar[])();
+ replaceInto(sink2, "äbö", 'ä', 'a');
+ assert(sink2.data == "abö");
}
/++
@@ -2198,6 +2677,9 @@ if (isOutputRange!(Sink, E) && isDynamicArray!(E[])
Returns:
A new array without changing the contents of `subject`.
+
+ See_Also:
+ $(REF substitute, std,algorithm,iteration) for a lazy replace.
+/
T[] replace(T, Range)(T[] subject, size_t from, size_t to, Range stuff)
if (isInputRange!Range &&
@@ -2207,7 +2689,7 @@ if (isInputRange!Range &&
static if (hasLength!Range && is(ElementEncodingType!Range : T))
{
import std.algorithm.mutation : copy;
- assert(from <= to);
+ assert(from <= to, "from must be before or equal to to");
immutable sliceLen = to - from;
auto retval = new Unqual!(T)[](subject.length - sliceLen + stuff.length);
retval[0 .. from] = subject[0 .. from];
@@ -2216,7 +2698,14 @@ if (isInputRange!Range &&
copy(stuff, retval[from .. from + stuff.length]);
retval[from + stuff.length .. $] = subject[to .. $];
- return cast(T[]) retval;
+ static if (is(T == const) || is(T == immutable))
+ {
+ return () @trusted { return cast(T[]) retval; } ();
+ }
+ else
+ {
+ return cast(T[]) retval;
+ }
}
else
{
@@ -2310,13 +2799,20 @@ if (isInputRange!Range &&
assert(replace(d, 5, 10, "⁴³²¹⁰"d) == "⁰¹²³⁴⁴³²¹⁰"d);
}
+// https://issues.dlang.org/show_bug.cgi?id=18166
+@safe pure unittest
+{
+ auto str = replace("aaaaa"d, 1, 4, "***"d);
+ assert(str == "a***a");
+}
+
/++
Replaces elements from `array` with indices ranging from `from`
(inclusive) to `to` (exclusive) with the range `stuff`. Expands or
shrinks the array as needed.
Params:
- array = the _array to scan
+ array = the array to scan
from = the starting index
to = the ending index
stuff = the items to replace in-between `from` and `to`
@@ -2373,9 +2869,9 @@ if (is(typeof(replace(array, from, to, stuff))))
assert(a == [1, 4, 5, 5]);
}
+// https://issues.dlang.org/show_bug.cgi?id=12889
@safe unittest
{
- // Bug# 12889
int[1][] arr = [[0], [1], [2], [3], [4], [5], [6]];
int[1][] stuff = [[0], [1]];
replaceInPlace(arr, 4, 6, stuff);
@@ -2384,7 +2880,7 @@ if (is(typeof(replace(array, from, to, stuff))))
@system unittest
{
- // Bug# 14925
+ // https://issues.dlang.org/show_bug.cgi?id=14925
char[] a = "mon texte 1".dup;
char[] b = "abc".dup;
replaceInPlace(a, 4, 9, b);
@@ -2451,8 +2947,7 @@ if (is(typeof(replace(array, from, to, stuff))))
import std.exception;
- bool test(T, U, V)(T orig, size_t from, size_t to, U toReplace, V result,
- string file = __FILE__, size_t line = __LINE__)
+ bool test(T, U, V)(T orig, size_t from, size_t to, U toReplace, V result)
{
{
static if (is(T == typeof(T.init.dup)))
@@ -2524,7 +3019,7 @@ if (is(typeof(replace(array, from, to, stuff))))
to = the item to replace `from` with
Returns:
- A new array without changing the contents of $(D subject), or the original
+ A new array without changing the contents of `subject`, or the original
array if no match is found.
+/
E[] replaceFirst(E, R1, R2)(E[] subject, R1 from, R2 to)
@@ -2580,12 +3075,12 @@ if (isDynamicArray!(E[]) &&
import std.algorithm.comparison : cmp;
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
+ static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
const(char[]), immutable(char[])))
{
- foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
+ static foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
const(char[]), immutable(char[])))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ {{
auto s = to!S("This is a foo foo list");
auto s2 = to!S("Thüs is a ßöö foo list");
auto from = to!T("foo");
@@ -2607,11 +3102,11 @@ if (isDynamicArray!(E[]) &&
assert(cmp(r3, "This is a foo foo list") == 0);
assert(replaceFirst(r3, to!T("won't find"), to!T("whatever")) is r3);
- }();
+ }}
}
}
-//Bug# 8187
+// https://issues.dlang.org/show_bug.cgi?id=8187
@safe unittest
{
auto res = ["a", "a"];
@@ -2628,7 +3123,7 @@ if (isDynamicArray!(E[]) &&
to = the item to replace `from` with
Returns:
- A new array without changing the contents of $(D subject), or the original
+ A new array without changing the contents of `subject`, or the original
array if no match is found.
+/
E[] replaceLast(E, R1, R2)(E[] subject, R1 from , R2 to)
@@ -2693,12 +3188,12 @@ if (isDynamicArray!(E[]) &&
import std.algorithm.comparison : cmp;
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
+ static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
const(char[]), immutable(char[])))
{
- foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
+ static foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
const(char[]), immutable(char[])))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ {{
auto s = to!S("This is a foo foo list");
auto s2 = to!S("Thüs is a ßöö ßöö list");
auto from = to!T("foo");
@@ -2720,7 +3215,7 @@ if (isDynamicArray!(E[]) &&
assert(cmp(r3, "This is a foo foo list") == 0);
assert(replaceLast(r3, to!T("won't find"), to!T("whatever")) is r3);
- }();
+ }}
}
}
@@ -2737,27 +3232,32 @@ if (isDynamicArray!(E[]) &&
Returns:
A new array that is `s` with `slice` replaced by
`replacement[]`.
+
+ See_Also:
+ $(REF substitute, std,algorithm,iteration) for a lazy replace.
+/
inout(T)[] replaceSlice(T)(inout(T)[] s, in T[] slice, in T[] replacement)
in
{
// Verify that slice[] really is a slice of s[]
- assert(overlap(s, slice) is slice);
+ assert(overlap(s, slice) is slice, "slice[] is not a subslice of s[]");
}
-body
+do
{
auto result = new T[s.length - slice.length + replacement.length];
- immutable so = slice.ptr - s.ptr;
+ immutable so = &slice[0] - &s[0];
result[0 .. so] = s[0 .. so];
result[so .. so + replacement.length] = replacement[];
result[so + replacement.length .. result.length] =
s[so + slice.length .. s.length];
- return cast(inout(T)[]) result;
+ return () @trusted inout {
+ return cast(inout(T)[]) result;
+ }();
}
///
-@system unittest
+@safe unittest
{
auto a = [1, 2, 3, 4, 5];
auto b = replaceSlice(a, a[1 .. 4], [0, 0, 0]);
@@ -2765,7 +3265,7 @@ body
assert(b == [1, 0, 0, 0, 5]);
}
-@system unittest
+@safe unittest
{
import std.algorithm.comparison : cmp;
@@ -2783,6 +3283,10 @@ Implements an output range that appends data to an array. This is
recommended over $(D array ~= data) when appending many elements because it is more
efficient. `Appender` maintains its own array metadata locally, so it can avoid
global locking for each append where $(LREF capacity) is non-zero.
+
+Params:
+ A = the array type to simulate.
+
See_Also: $(LREF appender)
*/
struct Appender(A)
@@ -2796,7 +3300,7 @@ if (isDynamicArray!A)
{
size_t capacity;
Unqual!T[] arr;
- bool canExtend = false;
+ bool tryExtendBlock = false;
}
private Data* _data;
@@ -2807,7 +3311,7 @@ if (isDynamicArray!A)
* it will be used by the appender. After initializing an appender on an array,
* appending to the original array will reallocate.
*/
- this(A arr) @trusted pure nothrow
+ this(A arr) @trusted
{
// initialize to a given array.
_data = new Data;
@@ -2834,8 +3338,11 @@ if (isDynamicArray!A)
* Reserve at least newCapacity elements for appending. Note that more elements
* may be reserved than requested. If `newCapacity <= capacity`, then nothing is
* done.
+ *
+ * Params:
+ * newCapacity = the capacity the `Appender` should have
*/
- void reserve(size_t newCapacity) @safe pure nothrow
+ void reserve(size_t newCapacity)
{
if (_data)
{
@@ -2853,7 +3360,7 @@ if (isDynamicArray!A)
* managed array can accommodate before triggering a reallocation). If any
* appending will reallocate, `0` will be returned.
*/
- @property size_t capacity() const @safe pure nothrow
+ @property size_t capacity() const
{
return _data ? _data.capacity : 0;
}
@@ -2862,7 +3369,7 @@ if (isDynamicArray!A)
* Use opSlice() from now on.
* Returns: The managed array.
*/
- @property inout(ElementEncodingType!A)[] data() inout @trusted pure nothrow
+ @property inout(T)[] data() inout @trusted
{
return this[];
}
@@ -2870,7 +3377,7 @@ if (isDynamicArray!A)
/**
* Returns: The managed array.
*/
- @property inout(ElementEncodingType!A)[] opSlice() inout @trusted pure nothrow
+ @property inout(T)[] opSlice() inout @trusted
{
/* @trusted operation:
* casting Unqual!T[] to inout(T)[]
@@ -2879,7 +3386,7 @@ if (isDynamicArray!A)
}
// ensure we can add nelems elements, resizing as necessary
- private void ensureAddable(size_t nelems) @trusted pure nothrow
+ private void ensureAddable(size_t nelems)
{
if (!_data)
_data = new Data;
@@ -2913,9 +3420,9 @@ if (isDynamicArray!A)
// have better access to the capacity field.
auto newlen = appenderNewCapacity!(T.sizeof)(_data.capacity, reqlen);
// first, try extending the current block
- if (_data.canExtend)
+ if (_data.tryExtendBlock)
{
- immutable u = GC.extend(_data.arr.ptr, nelems * T.sizeof, (newlen - len) * T.sizeof);
+ immutable u = (() @trusted => GC.extend(_data.arr.ptr, nelems * T.sizeof, (newlen - len) * T.sizeof))();
if (u)
{
// extend worked, update the capacity
@@ -2929,15 +3436,16 @@ if (isDynamicArray!A)
import core.checkedint : mulu;
bool overflow;
const nbytes = mulu(newlen, T.sizeof, overflow);
- if (overflow) assert(0);
+ if (overflow) assert(false, "the reallocation would exceed the "
+ ~ "available pointer range");
- auto bi = GC.qalloc(nbytes, blockAttribute!T);
+ auto bi = (() @trusted => GC.qalloc(nbytes, blockAttribute!T))();
_data.capacity = bi.size / T.sizeof;
import core.stdc.string : memcpy;
if (len)
- memcpy(bi.base, _data.arr.ptr, len * T.sizeof);
- _data.arr = (cast(Unqual!T*) bi.base)[0 .. len];
- _data.canExtend = true;
+ () @trusted { memcpy(bi.base, _data.arr.ptr, len * T.sizeof); }();
+ _data.arr = (() @trusted => (cast(Unqual!T*) bi.base)[0 .. len])();
+ _data.tryExtendBlock = true;
// leave the old data, for safety reasons
}
}
@@ -2945,7 +3453,7 @@ if (isDynamicArray!A)
private template canPutItem(U)
{
enum bool canPutItem =
- isImplicitlyConvertible!(U, T) ||
+ isImplicitlyConvertible!(Unqual!U, Unqual!T) ||
isSomeChar!T && isSomeChar!U;
}
private template canPutConstRange(Range)
@@ -2963,7 +3471,11 @@ if (isDynamicArray!A)
}
/**
- * Appends `item` to the managed array.
+ * Appends `item` to the managed array. Performs encoding for
+ * `char` types if `A` is a differently typed `char` array.
+ *
+ * Params:
+ * item = the single item to append
*/
void put(U)(U item) if (canPutItem!U)
{
@@ -2980,13 +3492,13 @@ if (isDynamicArray!A)
}
else
{
- import std.conv : emplaceRef;
+ import core.internal.lifetime : emplaceRef;
ensureAddable(1);
immutable len = _data.arr.length;
auto bigData = (() @trusted => _data.arr.ptr[0 .. len + 1])();
- emplaceRef!(Unqual!T)(bigData[len], cast(Unqual!T) item);
+ emplaceRef!(Unqual!T)(bigData[len], cast() item);
//We do this at the end, in case of exceptions
_data.arr = bigData;
}
@@ -3000,7 +3512,11 @@ if (isDynamicArray!A)
}
/**
- * Appends an entire range to the managed array.
+ * Appends an entire range to the managed array. Performs encoding for
+ * `char` elements if `A` is a differently typed `char` array.
+ *
+ * Params:
+ * items = the range of items to append
*/
void put(Range)(Range items) if (canPutRange!Range)
{
@@ -3023,10 +3539,10 @@ if (isDynamicArray!A)
}
// make sure we have enough space, then add the items
- @trusted auto bigDataFun(size_t extra)
+ auto bigDataFun(size_t extra)
{
ensureAddable(extra);
- return _data.arr.ptr[0 .. _data.arr.length + extra];
+ return (() @trusted => _data.arr.ptr[0 .. _data.arr.length + extra])();
}
auto bigData = bigDataFun(items.length);
@@ -3042,7 +3558,7 @@ if (isDynamicArray!A)
}
else
{
- import std.conv : emplaceRef;
+ import core.internal.lifetime : emplaceRef;
foreach (ref it ; bigData[len .. newlen])
{
emplaceRef!T(it, items.front);
@@ -3053,6 +3569,17 @@ if (isDynamicArray!A)
//We do this at the end, in case of exceptions
_data.arr = bigData;
}
+ else static if (isSomeChar!T && isSomeChar!(ElementType!Range) &&
+ !is(immutable T == immutable ElementType!Range))
+ {
+ // need to decode and encode
+ import std.utf : decodeFront;
+ while (!items.empty)
+ {
+ auto c = items.decodeFront;
+ put(c);
+ }
+ }
else
{
//pragma(msg, Range.stringof);
@@ -3065,15 +3592,11 @@ if (isDynamicArray!A)
}
/**
- * Appends `rhs` to the managed array.
- * Params:
- * rhs = Element or range.
+ * Appends to the managed array.
+ *
+ * See_Also: $(LREF Appender.put)
*/
- void opOpAssign(string op : "~", U)(U rhs)
- if (__traits(compiles, put(rhs)))
- {
- put(rhs);
- }
+ alias opOpAssign(string op : "~") = put;
// only allow overwriting data on non-immutable and non-const data
static if (isMutable!T)
@@ -3083,7 +3606,7 @@ if (isDynamicArray!A)
* for appending.
*
* Note: clear is disabled for immutable or const element types, due to the
- * possibility that $(D Appender) might overwrite immutable data.
+ * possibility that `Appender` might overwrite immutable data.
*/
void clear() @trusted pure nothrow
{
@@ -3096,7 +3619,7 @@ if (isDynamicArray!A)
/**
* Shrinks the managed array to the given length.
*
- * Throws: $(D Exception) if newlength is greater than the current array length.
+ * Throws: `Exception` if newlength is greater than the current array length.
* Note: shrinkTo is disabled for immutable or const element types.
*/
void shrinkTo(size_t newlength) @trusted pure
@@ -3112,15 +3635,60 @@ if (isDynamicArray!A)
}
}
- void toString(Writer)(scope Writer w)
+ /**
+ * Gives a string in the form of `Appender!(A)(data)`.
+ *
+ * Params:
+ * w = A `char` accepting
+ * $(REF_ALTTEXT output range, isOutputRange, std, range, primitives).
+ * fmt = A $(REF FormatSpec, std, format) which controls how the array
+ * is formatted.
+ * Returns:
+ * A `string` if `writer` is not set; `void` otherwise.
+ */
+ string toString()() const
{
- import std.format : formattedWrite;
- w.formattedWrite(typeof(this).stringof ~ "(%s)", data);
+ import std.format.spec : singleSpec;
+
+ auto app = appender!string();
+ auto spec = singleSpec("%s");
+ immutable len = _data ? _data.arr.length : 0;
+ // different reserve lengths because each element in a
+ // non-string-like array uses two extra characters for `, `.
+ static if (isSomeString!A)
+ {
+ app.reserve(len + 25);
+ }
+ else
+ {
+ // Multiplying by three is a very conservative estimate of
+ // length, as it assumes each element is only one char
+ app.reserve((len * 3) + 25);
+ }
+ toString(app, spec);
+ return app.data;
+ }
+
+ import std.format.spec : FormatSpec;
+
+ /// ditto
+ template toString(Writer)
+ if (isOutputRange!(Writer, char))
+ {
+ void toString(ref Writer w, scope const ref FormatSpec!char fmt) const
+ {
+ import std.format.write : formatValue;
+ import std.range.primitives : put;
+ put(w, Unqual!(typeof(this)).stringof);
+ put(w, '(');
+ formatValue(w, data, fmt);
+ put(w, ')');
+ }
}
}
///
-@safe unittest
+@safe pure nothrow unittest
{
auto app = appender!string();
string b = "abcdefg";
@@ -3135,17 +3703,30 @@ if (isDynamicArray!A)
assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
}
-@safe unittest
+@safe pure unittest
{
import std.format : format;
+ import std.format.spec : singleSpec;
+
auto app = appender!(int[])();
app.put(1);
app.put(2);
app.put(3);
assert("%s".format(app) == "Appender!(int[])(%s)".format([1,2,3]));
+
+ auto app2 = appender!string();
+ auto spec = singleSpec("%s");
+ app.toString(app2, spec);
+ assert(app2[] == "Appender!(int[])([1, 2, 3])");
+
+ auto app3 = appender!string();
+ spec = singleSpec("%(%04d, %)");
+ app.toString(app3, spec);
+ assert(app3[] == "Appender!(int[])(0001, 0002, 0003)");
}
-@safe unittest // issue 17251
+// https://issues.dlang.org/show_bug.cgi?id=17251
+@safe pure nothrow unittest
{
static struct R
{
@@ -3160,12 +3741,89 @@ if (isDynamicArray!A)
app.put(r[]);
}
+// https://issues.dlang.org/show_bug.cgi?id=13300
+@safe pure nothrow unittest
+{
+ static test(bool isPurePostblit)()
+ {
+ static if (!isPurePostblit)
+ static int i;
+
+ struct Simple
+ {
+ @disable this(); // Without this, it works.
+ static if (!isPurePostblit)
+ this(this) { i++; }
+ else
+ pure this(this) { }
+
+ private:
+ this(int tmp) { }
+ }
+
+ struct Range
+ {
+ @property Simple front() { return Simple(0); }
+ void popFront() { count++; }
+ @property empty() { return count < 3; }
+ size_t count;
+ }
+
+ Range r;
+ auto a = r.array();
+ }
+
+ static assert(__traits(compiles, () pure { test!true(); }));
+ static assert(!__traits(compiles, () pure { test!false(); }));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19572
+@safe pure nothrow unittest
+{
+ static struct Struct
+ {
+ int value;
+
+ int fun() const { return 23; }
+
+ alias fun this;
+ }
+
+ Appender!(Struct[]) appender;
+
+ appender.put(const(Struct)(42));
+
+ auto result = appender[][0];
+
+ assert(result.value != 23);
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.utf : byCodeUnit;
+ auto str = "ウェブサイト";
+ auto wstr = appender!wstring();
+ put(wstr, str.byCodeUnit);
+ assert(wstr.data == str.to!wstring);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21256
+@safe pure unittest
+{
+ Appender!string app1;
+ app1.toString();
+
+ Appender!(int[]) app2;
+ app2.toString();
+}
+
//Calculates an efficient growth scheme based on the old capacity
//of data, and the minimum requested capacity.
//arg curLen: The current length
//arg reqLen: The length as requested by the user
//ret sugLen: A suggested growth.
-private size_t appenderNewCapacity(size_t TSizeOf)(size_t curLen, size_t reqLen) @safe pure nothrow
+private size_t appenderNewCapacity(size_t TSizeOf)(size_t curLen, size_t reqLen)
{
import core.bitop : bsr;
import std.algorithm.comparison : max;
@@ -3186,10 +3844,15 @@ private size_t appenderNewCapacity(size_t TSizeOf)(size_t curLen, size_t reqLen)
* original array passed in.
*
* Tip: Use the `arrayPtr` overload of $(LREF appender) for construction with type-inference.
+ *
+ * Params:
+ * A = The array type to simulate
*/
struct RefAppender(A)
if (isDynamicArray!A)
{
+ private alias T = ElementEncodingType!A;
+
private
{
Appender!A impl;
@@ -3242,7 +3905,7 @@ if (isDynamicArray!A)
/**
* Returns the capacity of the array (the maximum number of elements the
* managed array can accommodate before triggering a reallocation). If any
- * appending will reallocate, $(D capacity) returns $(D 0).
+ * appending will reallocate, `capacity` returns `0`.
*/
@property size_t capacity() const
{
@@ -3252,7 +3915,7 @@ if (isDynamicArray!A)
/* Use opSlice() instead.
* Returns: the managed array.
*/
- @property inout(ElementEncodingType!A)[] data() inout
+ @property inout(T)[] data() inout
{
return impl[];
}
@@ -3267,7 +3930,7 @@ if (isDynamicArray!A)
}
///
-@system pure nothrow
+@safe pure nothrow
unittest
{
int[] a = [1, 2];
@@ -3285,7 +3948,7 @@ unittest
/++
Convenience function that returns an $(LREF Appender) instance,
- optionally initialized with $(D array).
+ optionally initialized with `array`.
+/
Appender!A appender(A)()
if (isDynamicArray!A)
@@ -3303,63 +3966,49 @@ Appender!(E[]) appender(A : E[], E)(auto ref A array)
@safe pure nothrow unittest
{
- import std.exception;
- {
- auto app = appender!(char[])();
- string b = "abcdefg";
- foreach (char c; b) app.put(c);
- assert(app[] == "abcdefg");
- }
- {
- auto app = appender!(char[])();
- string b = "abcdefg";
- foreach (char c; b) app ~= c;
- assert(app[] == "abcdefg");
- }
- {
- int[] a = [ 1, 2 ];
- auto app2 = appender(a);
- assert(app2[] == [ 1, 2 ]);
- app2.put(3);
- app2.put([ 4, 5, 6 ][]);
- assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
- app2.put([ 7 ]);
- assert(app2[] == [ 1, 2, 3, 4, 5, 6, 7 ]);
- }
+ auto app = appender!(char[])();
+ string b = "abcdefg";
+ foreach (char c; b) app.put(c);
+ assert(app[] == "abcdefg");
+}
+
+@safe pure nothrow unittest
+{
+ auto app = appender!(char[])();
+ string b = "abcdefg";
+ foreach (char c; b) app ~= c;
+ assert(app[] == "abcdefg");
+}
+@safe pure nothrow unittest
+{
int[] a = [ 1, 2 ];
auto app2 = appender(a);
assert(app2[] == [ 1, 2 ]);
- app2 ~= 3;
- app2 ~= [ 4, 5, 6 ][];
+ app2.put(3);
+ app2.put([ 4, 5, 6 ][]);
assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
- app2 ~= [ 7 ];
+ app2.put([ 7 ]);
assert(app2[] == [ 1, 2, 3, 4, 5, 6, 7 ]);
+}
- app2.reserve(5);
- assert(app2.capacity >= 5);
-
- try // shrinkTo may throw
- {
- app2.shrinkTo(3);
- }
- catch (Exception) assert(0);
- assert(app2[] == [ 1, 2, 3 ]);
- assertThrown(app2.shrinkTo(5));
-
- const app3 = app2;
- assert(app3.capacity >= 3);
- assert(app3[] == [1, 2, 3]);
-
+@safe pure nothrow unittest
+{
auto app4 = appender([]);
try // shrinkTo may throw
{
app4.shrinkTo(0);
}
catch (Exception) assert(0);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=5663
+// https://issues.dlang.org/show_bug.cgi?id=9725
+@safe pure nothrow unittest
+{
+ import std.exception : assertNotThrown;
- // Issue 5663 & 9725 tests
- foreach (S; AliasSeq!(char[], const(char)[], string))
+ static foreach (S; AliasSeq!(char[], const(char)[], string))
{
{
Appender!S app5663i;
@@ -3389,6 +4038,12 @@ Appender!(E[]) appender(A : E[], E)(auto ref A array)
assert(app5663m[] == "\xE3");
}
}
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=10122
+@safe pure nothrow unittest
+{
+ import std.exception : assertCTFEable;
static struct S10122
{
@@ -3405,6 +4060,35 @@ Appender!(E[]) appender(A : E[], E)(auto ref A array)
});
}
+@safe pure nothrow unittest
+{
+ import std.exception : assertThrown;
+
+ int[] a = [ 1, 2 ];
+ auto app2 = appender(a);
+ assert(app2[] == [ 1, 2 ]);
+ app2 ~= 3;
+ app2 ~= [ 4, 5, 6 ][];
+ assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
+ app2 ~= [ 7 ];
+ assert(app2[] == [ 1, 2, 3, 4, 5, 6, 7 ]);
+
+ app2.reserve(5);
+ assert(app2.capacity >= 5);
+
+ try // shrinkTo may throw
+ {
+ app2.shrinkTo(3);
+ }
+ catch (Exception) assert(0);
+ assert(app2[] == [ 1, 2, 3 ]);
+ assertThrown(app2.shrinkTo(5));
+
+ const app3 = app2;
+ assert(app3.capacity >= 3);
+ assert(app3[] == [1, 2, 3]);
+}
+
///
@safe pure nothrow
unittest
@@ -3426,63 +4110,63 @@ unittest
@safe pure nothrow unittest
{
+ auto w = appender!string();
+ w.reserve(4);
+ cast(void) w.capacity;
+ cast(void) w[];
+ try
{
- auto w = appender!string();
- w.reserve(4);
- cast(void) w.capacity;
- cast(void) w[];
- try
- {
- wchar wc = 'a';
- dchar dc = 'a';
- w.put(wc); // decoding may throw
- w.put(dc); // decoding may throw
- }
- catch (Exception) assert(0);
+ wchar wc = 'a';
+ dchar dc = 'a';
+ w.put(wc); // decoding may throw
+ w.put(dc); // decoding may throw
}
+ catch (Exception) assert(0);
+}
+
+@safe pure nothrow unittest
+{
+ auto w = appender!(int[])();
+ w.reserve(4);
+ cast(void) w.capacity;
+ cast(void) w[];
+ w.put(10);
+ w.put([10]);
+ w.clear();
+ try
{
- auto w = appender!(int[])();
- w.reserve(4);
- cast(void) w.capacity;
- cast(void) w[];
- w.put(10);
- w.put([10]);
- w.clear();
- try
- {
- w.shrinkTo(0);
- }
- catch (Exception) assert(0);
+ w.shrinkTo(0);
+ }
+ catch (Exception) assert(0);
- struct N
- {
- int payload;
- alias payload this;
- }
- w.put(N(1));
- w.put([N(2)]);
+ struct N
+ {
+ int payload;
+ alias payload this;
+ }
+ w.put(N(1));
+ w.put([N(2)]);
- struct S(T)
- {
- @property bool empty() { return true; }
- @property T front() { return T.init; }
- void popFront() {}
- }
- S!int r;
- w.put(r);
+ struct S(T)
+ {
+ @property bool empty() { return true; }
+ @property T front() { return T.init; }
+ void popFront() {}
}
+ S!int r;
+ w.put(r);
}
-@safe unittest
+// https://issues.dlang.org/show_bug.cgi?id=10690
+@safe pure nothrow unittest
{
- import std.algorithm;
- import std.typecons;
- //10690
+ import std.algorithm.iteration : filter;
+ import std.typecons : tuple;
[tuple(1)].filter!(t => true).array; // No error
[tuple("A")].filter!(t => true).array; // error
}
-@system unittest
+@safe pure nothrow unittest
{
import std.range;
//Coverage for put(Range)
@@ -3496,16 +4180,25 @@ unittest
auto a1 = Appender!(S1[])();
auto a2 = Appender!(S2[])();
auto au1 = Appender!(const(S1)[])();
- auto au2 = Appender!(const(S2)[])();
a1.put(S1().repeat().take(10));
a2.put(S2().repeat().take(10));
auto sc1 = const(S1)();
- auto sc2 = const(S2)();
au1.put(sc1.repeat().take(10));
+}
+
+@system pure unittest
+{
+ import std.range;
+ struct S2
+ {
+ void opAssign(S2){}
+ }
+ auto au2 = Appender!(const(S2)[])();
+ auto sc2 = const(S2)();
au2.put(sc2.repeat().take(10));
}
-@system unittest
+@system pure nothrow unittest
{
struct S
{
@@ -3538,9 +4231,9 @@ unittest
a2.put([s2]);
}
-@safe unittest
+// https://issues.dlang.org/show_bug.cgi?id=9528
+@safe pure nothrow unittest
{
- //9528
const(E)[] fastCopy(E)(E[] src) {
auto app = appender!(const(E)[])();
foreach (i, e; src)
@@ -3553,12 +4246,13 @@ unittest
S[] s = [ S(new C) ];
auto t = fastCopy(s); // Does not compile
+ assert(t.length == 1);
}
-@safe unittest
+// https://issues.dlang.org/show_bug.cgi?id=10753
+@safe pure unittest
{
import std.algorithm.iteration : map;
- //10753
struct Foo {
immutable dchar d;
}
@@ -3569,7 +4263,7 @@ unittest
[1, 2].map!Bar.array;
}
-@safe unittest
+@safe pure nothrow unittest
{
import std.algorithm.comparison : equal;
@@ -3618,7 +4312,7 @@ unittest
assert(app8[] == null);
}
-@safe unittest //Test large allocations (for GC.extend)
+@safe pure nothrow unittest //Test large allocations (for GC.extend)
{
import std.algorithm.comparison : equal;
import std.range;
@@ -3629,7 +4323,7 @@ unittest
assert(equal(app[], 'a'.repeat(100_000)));
}
-@safe unittest
+@safe pure nothrow unittest
{
auto reference = new ubyte[](2048 + 1); //a number big enough to have a full page (EG: the GC extends)
auto arr = reference.dup;
@@ -3639,7 +4333,7 @@ unittest
assert(reference[] == arr[]);
}
-@safe unittest // clear method is supported only for mutable element types
+@safe pure nothrow unittest // clear method is supported only for mutable element types
{
Appender!string app;
app.put("foo");
@@ -3647,7 +4341,7 @@ unittest
assert(app[] == "foo");
}
-@safe unittest
+@safe pure nothrow unittest
{
static struct D//dynamic
{
@@ -3678,7 +4372,7 @@ unittest
@system unittest
{
- // Issue 13077
+ // https://issues.dlang.org/show_bug.cgi?id=13077
static class A {}
// reduced case
@@ -3692,12 +4386,13 @@ unittest
return [new shared A].inputRangeObject;
}
auto res = foo.array;
+ assert(res.length == 1);
}
/++
Convenience function that returns a $(LREF RefAppender) instance initialized
with `arrayPtr`. Don't use null for the array pointer, use the other
- version of $(D appender) instead.
+ version of `appender` instead.
+/
RefAppender!(E[]) appender(P : E[]*, E)(P arrayPtr)
{
@@ -3705,7 +4400,7 @@ RefAppender!(E[]) appender(P : E[]*, E)(P arrayPtr)
}
///
-@system pure nothrow
+@safe pure nothrow
unittest
{
int[] a = [1, 2];
@@ -3721,35 +4416,41 @@ unittest
assert(app2.capacity >= 5);
}
-@system unittest
+@safe pure nothrow unittest
{
- import std.exception;
- {
- auto arr = new char[0];
- auto app = appender(&arr);
- string b = "abcdefg";
- foreach (char c; b) app.put(c);
- assert(app[] == "abcdefg");
- assert(arr == "abcdefg");
- }
- {
- auto arr = new char[0];
- auto app = appender(&arr);
- string b = "abcdefg";
- foreach (char c; b) app ~= c;
- assert(app[] == "abcdefg");
- assert(arr == "abcdefg");
- }
- {
- int[] a = [ 1, 2 ];
- auto app2 = appender(&a);
- assert(app2[] == [ 1, 2 ]);
- assert(a == [ 1, 2 ]);
- app2.put(3);
- app2.put([ 4, 5, 6 ][]);
- assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
- assert(a == [ 1, 2, 3, 4, 5, 6 ]);
- }
+ auto arr = new char[0];
+ auto app = appender(&arr);
+ string b = "abcdefg";
+ foreach (char c; b) app.put(c);
+ assert(app[] == "abcdefg");
+ assert(arr == "abcdefg");
+}
+
+@safe pure nothrow unittest
+{
+ auto arr = new char[0];
+ auto app = appender(&arr);
+ string b = "abcdefg";
+ foreach (char c; b) app ~= c;
+ assert(app[] == "abcdefg");
+ assert(arr == "abcdefg");
+}
+
+@safe pure nothrow unittest
+{
+ int[] a = [ 1, 2 ];
+ auto app2 = appender(&a);
+ assert(app2[] == [ 1, 2 ]);
+ assert(a == [ 1, 2 ]);
+ app2.put(3);
+ app2.put([ 4, 5, 6 ][]);
+ assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
+ assert(a == [ 1, 2, 3, 4, 5, 6 ]);
+}
+
+@safe pure nothrow unittest
+{
+ import std.exception : assertThrown;
int[] a = [ 1, 2 ];
auto app2 = appender(&a);
@@ -3776,13 +4477,14 @@ unittest
assert(app3[] == [1, 2, 3]);
}
-@safe unittest // issue 14605
+// https://issues.dlang.org/show_bug.cgi?id=14605
+@safe pure nothrow unittest
{
static assert(isOutputRange!(Appender!(int[]), int));
static assert(isOutputRange!(RefAppender!(int[]), int));
}
-@safe unittest
+@safe pure nothrow unittest
{
Appender!(int[]) app;
short[] range = [1, 2, 3];
@@ -3790,7 +4492,7 @@ unittest
assert(app[] == [1, 2, 3]);
}
-@safe unittest
+@safe pure nothrow unittest
{
string s = "hello".idup;
char[] a = "hello".dup;
@@ -3803,3 +4505,269 @@ unittest
assert(appS[] == "hellow");
assert(appA[] == "hellow");
}
+
+/++
+Constructs a static array from `a`.
+The type of elements can be specified implicitly so that $(D [1, 2].staticArray) results in `int[2]`,
+or explicitly, e.g. $(D [1, 2].staticArray!float) returns `float[2]`.
+When `a` is a range whose length is not known at compile time, the number of elements must be
+given as template argument (e.g. `myrange.staticArray!2`).
+Size and type can be combined, if the source range elements are implicitly
+convertible to the requested element type (eg: `2.iota.staticArray!(long[2])`).
+When the range `a` is known at compile time, it can also be specified as a
+template argument to avoid having to specify the number of elements
+(e.g.: `staticArray!(2.iota)` or `staticArray!(double, 2.iota)`).
+
+Note: `staticArray` returns by value, so expressions involving large arrays may be inefficient.
+
+Params:
+ a = The input elements. If there are less elements than the specified length of the static array,
+ the rest of it is default-initialized. If there are more than specified, the first elements
+ up to the specified length are used.
+ rangeLength = outputs the number of elements used from `a` to it. Optional.
+
+Returns: A static array constructed from `a`.
++/
+pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a)
+{
+ return a;
+}
+
+/// static array from array literal
+nothrow pure @safe @nogc unittest
+{
+ auto a = [0, 1].staticArray;
+ static assert(is(typeof(a) == int[2]));
+ assert(a == [0, 1]);
+}
+
+pragma(inline, true) U[n] staticArray(U, T, size_t n)(auto ref T[n] a)
+if (!is(T == U) && is(T : U))
+{
+ return a[].staticArray!(U[n]);
+}
+
+/// static array from array with implicit casting of elements
+nothrow pure @safe @nogc unittest
+{
+ auto b = [0, 1].staticArray!long;
+ static assert(is(typeof(b) == long[2]));
+ assert(b == [0, 1]);
+}
+
+nothrow pure @safe @nogc unittest
+{
+ int val = 3;
+ static immutable gold = [1, 2, 3];
+ [1, 2, val].staticArray.checkStaticArray!int([1, 2, 3]);
+
+ @nogc void checkNogc()
+ {
+ [1, 2, val].staticArray.checkStaticArray!int(gold);
+ }
+
+ checkNogc();
+
+ [1, 2, val].staticArray!double.checkStaticArray!double(gold);
+ [1, 2, 3].staticArray!int.checkStaticArray!int(gold);
+
+ [1, 2, 3].staticArray!(const(int)).checkStaticArray!(const(int))(gold);
+ [1, 2, 3].staticArray!(const(double)).checkStaticArray!(const(double))(gold);
+ {
+ const(int)[3] a2 = [1, 2, 3].staticArray;
+ }
+
+ [cast(byte) 1, cast(byte) 129].staticArray.checkStaticArray!byte([1, -127]);
+}
+
+/// ditto
+auto staticArray(size_t n, T)(scope T a)
+if (isInputRange!T)
+{
+ alias U = ElementType!T;
+ return staticArray!(U[n], U, n)(a);
+}
+
+/// ditto
+auto staticArray(size_t n, T)(scope T a, out size_t rangeLength)
+if (isInputRange!T)
+{
+ alias U = ElementType!T;
+ return staticArray!(U[n], U, n)(a, rangeLength);
+}
+
+/// ditto
+auto staticArray(Un : U[n], U, size_t n, T)(scope T a)
+if (isInputRange!T && is(ElementType!T : U))
+{
+ size_t extraStackSpace;
+ return staticArray!(Un, U, n)(a, extraStackSpace);
+}
+
+/// ditto
+auto staticArray(Un : U[n], U, size_t n, T)(scope T a, out size_t rangeLength)
+if (isInputRange!T && is(ElementType!T : U))
+{
+ import std.algorithm.mutation : uninitializedFill;
+ import std.range : take;
+ import core.internal.lifetime : emplaceRef;
+
+ if (__ctfe)
+ {
+ size_t i;
+ // Compile-time version to avoid unchecked memory access.
+ Unqual!U[n] ret;
+ for (auto iter = a.take(n); !iter.empty; iter.popFront())
+ {
+ ret[i] = iter.front;
+ i++;
+ }
+
+ rangeLength = i;
+ return (() @trusted => cast(U[n]) ret)();
+ }
+
+ auto ret = (() @trusted
+ {
+ Unqual!U[n] theArray = void;
+ return theArray;
+ }());
+
+ size_t i;
+ if (true)
+ {
+ // ret was void-initialized so let's initialize the unfilled part manually.
+ // also prevents destructors to be called on uninitialized memory if
+ // an exception is thrown
+ scope (exit) ret[i .. $].uninitializedFill(U.init);
+
+ for (auto iter = a.take(n); !iter.empty; iter.popFront())
+ {
+ emplaceRef!U(ret[i++], iter.front);
+ }
+ }
+
+ rangeLength = i;
+ return (() @trusted => cast(U[n]) ret)();
+}
+
+/// static array from range + size
+nothrow pure @safe @nogc unittest
+{
+ import std.range : iota;
+
+ auto input = 3.iota;
+ auto a = input.staticArray!2;
+ static assert(is(typeof(a) == int[2]));
+ assert(a == [0, 1]);
+ auto b = input.staticArray!(long[4]);
+ static assert(is(typeof(b) == long[4]));
+ assert(b == [0, 1, 2, 0]);
+}
+
+// Tests that code compiles when there is an elaborate destructor and exceptions
+// are thrown. Unfortunately can't test that memory is initialized
+// before having a destructor called on it.
+@safe nothrow unittest
+{
+ // exists only to allow doing something in the destructor. Not tested
+ // at the end because value appears to depend on implementation of the.
+ // function.
+ static int preventersDestroyed = 0;
+
+ static struct CopyPreventer
+ {
+ bool on = false;
+ this(this)
+ {
+ if (on) throw new Exception("Thou shalt not copy past me!");
+ }
+
+ ~this()
+ {
+ preventersDestroyed++;
+ }
+ }
+ auto normalArray =
+ [
+ CopyPreventer(false),
+ CopyPreventer(false),
+ CopyPreventer(true),
+ CopyPreventer(false),
+ CopyPreventer(true),
+ ];
+
+ try
+ {
+ auto staticArray = normalArray.staticArray!5;
+ assert(false);
+ }
+ catch (Exception e){}
+}
+
+
+nothrow pure @safe @nogc unittest
+{
+ auto a = [1, 2].staticArray;
+ assert(is(typeof(a) == int[2]) && a == [1, 2]);
+
+ import std.range : iota;
+
+ 2.iota.staticArray!2.checkStaticArray!int([0, 1]);
+ 2.iota.staticArray!(double[2]).checkStaticArray!double([0, 1]);
+ 2.iota.staticArray!(long[2]).checkStaticArray!long([0, 1]);
+}
+
+nothrow pure @safe @nogc unittest
+{
+ import std.range : iota;
+ size_t copiedAmount;
+ 2.iota.staticArray!1(copiedAmount);
+ assert(copiedAmount == 1);
+ 2.iota.staticArray!3(copiedAmount);
+ assert(copiedAmount == 2);
+}
+
+/// ditto
+auto staticArray(alias a)()
+if (isInputRange!(typeof(a)))
+{
+ return .staticArray!(size_t(a.length))(a);
+}
+
+/// ditto
+auto staticArray(U, alias a)()
+if (isInputRange!(typeof(a)))
+{
+ return .staticArray!(U[size_t(a.length)])(a);
+}
+
+/// static array from CT range
+nothrow pure @safe @nogc unittest
+{
+ import std.range : iota;
+
+ enum a = staticArray!(2.iota);
+ static assert(is(typeof(a) == int[2]));
+ assert(a == [0, 1]);
+
+ enum b = staticArray!(long, 2.iota);
+ static assert(is(typeof(b) == long[2]));
+ assert(b == [0, 1]);
+}
+
+nothrow pure @safe @nogc unittest
+{
+ import std.range : iota;
+
+ enum a = staticArray!(2.iota);
+ staticArray!(2.iota).checkStaticArray!int([0, 1]);
+ staticArray!(double, 2.iota).checkStaticArray!double([0, 1]);
+ staticArray!(long, 2.iota).checkStaticArray!long([0, 1]);
+}
+
+version (StdUnittest) private void checkStaticArray(T, T1, T2)(T1 a, T2 b) nothrow @safe pure @nogc
+{
+ static assert(is(T1 == T[T1.length]));
+ assert(a == b, "a must be equal to b");
+}